이것은 제가 관심이있는 주제입니다. EF와 NHibernate와 같은 기술을 테스트해서는 안된다고 말하는 많은 순수 주의자들이 있습니다. 그들은 옳고 이미 엄격하게 테스트를 거쳤으며 이전 답변에서 언급했듯이 소유하지 않은 것을 테스트하는 데 많은 시간을 소비하는 것이 무의미합니다.
그러나 아래 데이터베이스를 소유하고 있습니다! 이것은 내 의견으로는이 접근법이 세분화 된 곳이므로 EF / NH가 올바르게 작업하고 있는지 테스트 할 필요가 없습니다. 맵핑 / 구현이 데이터베이스에서 작동하는지 테스트해야합니다. 제 생각에는 이것이 테스트 할 수있는 시스템의 가장 중요한 부분 중 하나입니다.
엄밀히 말하면 우리는 단위 테스트 영역에서 통합 테스트 영역으로 이동하고 있지만 원칙은 동일합니다.
가장 먼저해야 할 일은 DAL을 조롱하여 BLL을 EF 및 SQL과 독립적으로 테스트 할 수 있도록하는 것입니다. 이것들은 단위 테스트입니다. 다음으로 DAL을 증명하기 위해 통합 테스트 를 설계해야합니다 .
고려해야 할 몇 가지 사항이 있습니다.
- 데이터베이스는 각 테스트마다 알려진 상태에 있어야합니다. 대부분의 시스템은 백업을 사용하거나이를위한 스크립트를 만듭니다.
- 각 테스트는 반복 가능해야합니다
- 각 테스트는 원 자성이어야합니다
데이터베이스 설정에는 두 가지 주요 접근 방식이 있으며, 첫 번째는 UnitTest create DB 스크립트를 실행하는 것입니다. 이렇게하면 단위 테스트 데이터베이스가 각 테스트 시작시 항상 동일한 상태에있게됩니다 (이를 보장하기 위해이를 재설정하거나 트랜잭션에서 각 테스트를 실행할 수 있음).
다른 옵션은 내가하는 일입니다. 각 개별 테스트마다 특정 설정을 실행하십시오. 나는 이것이 두 가지 주요 이유로 가장 좋은 방법이라고 생각합니다.
- 데이터베이스가 더 간단합니다. 각 테스트마다 전체 스키마가 필요하지 않습니다.
- 각 테스트는 더 안전합니다. 작성 스크립트에서 하나의 값을 변경해도 수십 개의 다른 테스트는 무효화되지 않습니다.
불행히도 여기서 타협은 속도입니다. 이러한 모든 테스트 / 실행 스크립트를 실행하려면 시간이 걸립니다.
마지막으로 ORM을 테스트하기 위해 많은 양의 SQL을 작성하는 것은 매우 어려운 일입니다. 이것은 내가 매우 불쾌한 접근법을 취하는 곳입니다 (여기서 순수 주의자들은 저와 동의하지 않을 것입니다). ORM을 사용하여 테스트를 작성합니다! 내 시스템의 모든 DAL 테스트에 대해 별도의 스크립트를 사용하는 대신 객체를 생성하고 컨텍스트에 연결하여 저장하는 테스트 설정 단계가 있습니다. 그런 다음 테스트를 실행합니다.
이것은 이상적인 솔루션과는 거리가 멀지 만 실제로는 관리하기가 훨씬 쉽고 (특히 수천 번의 테스트가있을 때) 그렇지 않으면 대량의 스크립트를 작성합니다. 순도에 대한 실용성.
나는 몇 년 (수개월 / 일) 후에이 답을 되돌아보고 나의 접근 방식이 변함에 따라 나 자신의 의견에 동의하지 않을 것이다. 그러나 이것이 나의 현재 접근 방법이다.
위에서 말한 모든 것을 시도하고 요약하면 일반적인 DB 통합 테스트입니다.
[Test]
public void LoadUser()
{
this.RunTest(session => // the NH/EF session to attach the objects to
{
var user = new UserAccount("Mr", "Joe", "Bloggs");
session.Save(user);
return user.UserID;
}, id => // the ID of the entity we need to load
{
var user = LoadMyUser(id); // load the entity
Assert.AreEqual("Mr", user.Title); // test your properties
Assert.AreEqual("Joe", user.Firstname);
Assert.AreEqual("Bloggs", user.Lastname);
}
}
여기서 주목해야 할 것은 두 루프의 세션이 완전히 독립적이라는 것입니다. RunTest를 구현할 때 컨텍스트가 커밋되고 삭제되고 데이터가 두 번째 부분의 데이터베이스에서만 나올 수 있는지 확인해야합니다.
2014 년 10 월 13 일 수정
나는 아마 앞으로 몇 달 동안이 모델을 개정 할 것이라고 말했다. 위에서 주장한 접근 방식에 크게 의존하는 동안 테스트 메커니즘을 약간 업데이트했습니다. 나는 이제 TestSetup 및 TestTearDown에서 엔티티를 만드는 경향이 있습니다.
[SetUp]
public void Setup()
{
this.SetupTest(session => // the NH/EF session to attach the objects to
{
var user = new UserAccount("Mr", "Joe", "Bloggs");
session.Save(user);
this.UserID = user.UserID;
});
}
[TearDown]
public void TearDown()
{
this.TearDownDatabase();
}
그런 다음 각 속성을 개별적으로 테스트하십시오.
[Test]
public void TestTitle()
{
var user = LoadMyUser(this.UserID); // load the entity
Assert.AreEqual("Mr", user.Title);
}
[Test]
public void TestFirstname()
{
var user = LoadMyUser(this.UserID);
Assert.AreEqual("Joe", user.Firstname);
}
[Test]
public void TestLastname()
{
var user = LoadMyUser(this.UserID);
Assert.AreEqual("Bloggs", user.Lastname);
}
이 방법에는 몇 가지 이유가 있습니다.
- 추가 데이터베이스 호출이 없습니다 (하나의 설정, 하나의 분해)
- 테스트는 훨씬 세분화되며 각 테스트는 하나의 속성을 확인합니다
- 테스트 방법 자체에서 Setup / TearDown 논리가 제거되었습니다.
나는 이것이 테스트 클래스를 더 간단하게 만들고 테스트를 더 세밀하게 만든다고 생각 한다 ( 단일 주장은 좋다 )
2015 년 5 월 3 일 수정
이 접근법에 대한 또 다른 개정. 클래스 레벨 설정은 속성로드와 같은 테스트에 매우 유용하지만 다른 설정이 필요한 경우 유용하지 않습니다. 이 경우 각 사례에 대해 새 클래스를 설정하는 것은 과잉입니다.
이 I에 도움이 이제 두 개의 기본 클래스를 갖는 경향이 SetupPerTest
와 SingleSetup
. 이 두 클래스는 필요에 따라 프레임 워크를 노출합니다.
에서 SingleSetup
처음 편집에 설명 된대로 우리는 매우 유사한 메커니즘을 가지고있다. 예를 들면
public TestProperties : SingleSetup
{
public int UserID {get;set;}
public override DoSetup(ISession session)
{
var user = new User("Joe", "Bloggs");
session.Save(user);
this.UserID = user.UserID;
}
[Test]
public void TestLastname()
{
var user = LoadMyUser(this.UserID); // load the entity
Assert.AreEqual("Bloggs", user.Lastname);
}
[Test]
public void TestFirstname()
{
var user = LoadMyUser(this.UserID);
Assert.AreEqual("Joe", user.Firstname);
}
}
그러나 올바른 엔터티 만로드되도록하는 참조는 SetupPerTest 방식을 사용할 수 있습니다.
public TestProperties : SetupPerTest
{
[Test]
public void EnsureCorrectReferenceIsLoaded()
{
int friendID = 0;
this.RunTest(session =>
{
var user = CreateUserWithFriend();
session.Save(user);
friendID = user.Friends.Single().FriendID;
} () =>
{
var user = GetUser();
Assert.AreEqual(friendID, user.Friends.Single().FriendID);
});
}
[Test]
public void EnsureOnlyCorrectFriendsAreLoaded()
{
int userID = 0;
this.RunTest(session =>
{
var user = CreateUserWithFriends(2);
var user2 = CreateUserWithFriends(5);
session.Save(user);
session.Save(user2);
userID = user.UserID;
} () =>
{
var user = GetUser(userID);
Assert.AreEqual(2, user.Friends.Count());
});
}
}
요약하면 두 방법 모두 테스트하려는 대상에 따라 작동합니다.