在内存中模拟LinqToSql存储库以用于单元测试

时间:2012-04-30 00:07:55

标签: c# .net unit-testing linq-to-sql mocking

我正在研究制作一个可以在单元测试中使用的可模拟DataContext。描述了一种方法here。但是,此方法的问题是对存储库的更改会立即生效 - 在Commit(或任何其他等效于SubmitChanges)之前调用。

另一方面,要包含正确的SubmitChanges行为,可能会导致DataContext复制大量复杂代码,并可能导致更多错误。

使用单元测试的模拟内存存储库(不等待SubmitChanges)的天真实现是否可行?这通常是怎么做的?

3 个答案:

答案 0 :(得分:4)

如果我的理解是正确的,你还没有完全确定你的SUT(受试者)。

  • 如果您想测试与数据库的连接,那么我们正在谈论集成测试,您不应该嘲笑您的DataContext

  • 如果你想测试调用DataContext逻辑那么它是一个单元测试

在任何情况下,我都建议您将调用包装到存储库中的DataContext,存储库将负责 talk 到数据库,并遵循此方法将使事情比较容易

我将通过一个示例来澄清它,最后我将向您推荐几个可以帮助您编写可测试代码的链接。 (我总是说这个,我会再说一遍,写测试很容易,真正的努力必须放在写干净的可测试代码中)

public interface IMyRepository
{
   void ChangeEmail(int employeeId, string newEmail);
}
public class MyRepository : IMyRepository
{
   private MyDataContext context;
   public MyRepository(MyDataContext context)
   {
      this.context = context;
   }
   public void ChangeEmail(int employeeId, string newEmail)
   {
      //save your email using your context
   }
}

现在,在您的消费者代码中,您将注入您的存储库:

public class MyCommand
{
   public MyCommand(IMyRepository myRepository)
   ...
   public void ChangeEmail(int employeeId, string newEmail)
   {
      //adding condition just to clarify how to test
      if(this.AllowChangeEmail(employeeId))
      {
         this.myRepository.ChangeEmail(employeeId, newEmail);
      }
      else
      {
         throw new DomainException("this should not happen");
      }
   }
   ...
}

我们已经解析了DataContext的使用,从您的域代码的角度来看,DataContext不存在,域知道的唯一代码是IMyRepository,因为它是一个界面,您可以更改提供程序,轻松更改应用程序的行为,而无需重构您的域代码

如果您注意到我还没有谈过测试,为什么?因为就像我说的那样,要做的第一件事就是编写干净的可测试代码(不要误解这一点,应该遵循TDD,这意味着应该首先编写测试,我正在按照这种方法进行演示)现在我们有这个代码让我们看看测试应用程序的逻辑是多么容易我将手动编写IMyRepository的存根来澄清,但在实际代码中应该使用自动模拟对象,使用AutoFixtureFluentAssertionsMoq

等工具
public class MyFakeProvider : IMyRepository
{
   public void ChangeEmail(int employeeId, string newEmail)
   {
      //write some assert here indicating the method was called
   }
}

[Test]
public void MyTest()
{
   var myMock = new MyFakeProvider();
   var sut = new MyCommand(myMock);

   sut.Invoking(x => x.ChangeEmail(3, "my@email.com")).ShouldNotThrow();
}

这些链接仅关注一件事:编写可测试的代码:

http://misko.hevery.com/code-reviewers-guide/

http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf

http://misko.hevery.com/presentations/

http://www.youtube.com/watch?v=wEhu57pih5w&feature=player_embedded

http://www.youtube.com/watch?v=RlfLCWKxHJ0&feature=player_embedded

http://www.youtube.com/watch?v=-FRm3VPhseI&feature=player_embedded

http://www.youtube.com/watch?v=4F72VULWFvc&feature=player_embedded

答案 1 :(得分:2)

测试中的类/组件是什么?如果您没有直接测试存储库并且只测试使用存储库的东西,则模拟可以根据您的需要进行基本操作。

但是,如果您想在组件和存储库之间端到端地执行集成测试,则需要另一种方法,例如。针对测试数据库运行。

答案 2 :(得分:1)

我创建了一个表示存储库的接口,它有SubmitChanges,InsertOnSubmit等.DataContext实现的东西。此外,您的表的IQueryable属性。然后,您的DataContext子类可以实现此接口,您不需要执行任何操作。对于单元测试,您可以创建一个模拟,并模仿IQueryables,您可以创建列表并使用AsQueryable方法。