单元测试存储库需要帮助/建议

时间:2010-12-09 13:31:57

标签: c# .net unit-testing nunit rhino-mocks

我正在使用.NET 4,NUnit和Rhino模拟器。我想对我的新闻库进行单元测试,但我不确定如何去做。我的新闻存储库是我最终将用于与数据库通信的内容。我想用它来测试假/伪数据。不确定是否可能?这就是我目前所拥有的:

public interface INewsRepository
{
   IEnumerable<News> FindAll();
}

public class NewsRepository : INewsRepository
{
   private readonly INewsRepository newsRepository;

   public NewsRepository(INewsRepository newsRepository)
   {
      this.newsRepository = newsRepository;
   }

   public IEnumerable<News> FindAll()
   {
      return null;
   }
}

我的单元测试如下:

public class NewsRepositoryTest
{
   private INewsRepository newsRepository;

   [SetUp]
   public void Init()
   {
      newsRepository = MockRepository.GenerateMock<NewsRepository>();
   }

   [Test]
   public void FindAll_should_return_correct_news()
   {
      // Arrange
      List<News> newsList = new List<News>();
      newsList.Add(new News { Id = 1, Title = "Test Title 1" });
      newsList.Add(new News { Id = 2, Title = "Test Title 2" });

      newsRepository.Stub(r => r.FindAll()).Return(newsList);

      // Act
      var actual = newsRepository.FindAll();

      // Assert
      Assert.AreEqual(2, actual.Count());
   }
}

在上面的代码中,我不确定我需要模拟什么。上面的代码编译但在NUnit GUI中关于构造函数值失败。我只能假设它与我需要提供给NewsRepository的INewsRepository参数有关。我不知道如何在测试中做到这一点。有人可以纠正我的单元测试,以便它将通过NUnit GUI吗?如果我正确地实现我的存储库,有人也可以提供一些反馈吗?

作为嘲笑的新手,有什么我需要验证的吗?我什么时候需要验证?它的目的是什么?我一直在研究几个源代码项目,有些使用验证,有些则没有。

如果上述测试通过,这对我作为开发人员的证明是什么?另一个开发人员必须对我的存储库做什么才能使其在NUnit GUI中失败?

对不起所有问题,但它们是新手问题:)

我希望soomeone可以帮助我。

3 个答案:

答案 0 :(得分:5)

正如Steven所说,你在上面的代码中Assert反对Mock NewsRepository。

模拟的想法是隔离“受测试代码”并创建虚假以替换其依赖项

您使用Mock NewsRepository来测试使用INewsRepository 的内容,在您的情况下,您提到NewsService; NewsService将使用您的INewsRepository模拟。

如果您在解决方案中搜索任何使用INewsRepository.FindAll()的内容,您将创建一个模拟存储库来单独测试该代码。

如果您想测试调用服务图层的内容,则需要模拟NewsService

另外,正如Steven所说,NewsRepository不需要IoC注入自己的副本,所以:

public class NewsRepository : INewsRepository
{
   private readonly INewsRepository newsRepository;

   public NewsRepository(INewsRepository newsRepository)
   {
      this.newsRepository = newsRepository;
   }

   public IEnumerable<News> FindAll()
   {
      return null;
   }
}

应该成为:

public class NewsRepository : INewsRepository
{
   public IEnumerable<News> FindAll()
   {
      return null;
   }
}

在FindAll()方法中有需要测试的功能后,您可以模拟他们使用的对象

作为模拟对象的大Art Of Unit Testing初始化的样式点,最好不要使用Setup方法,而是在方法开头调用的辅助方法中执行。由于对安装程序的调用将是不可见的,并使模拟的初始化不清楚。

作为另一种风格,从该书中,建议的单元测试命名约定是:“ MethodUnderTest_Scenario_ExpectedBehavior ”。 所以,

FindAll_should_return_correct_news
可能会变成,例如:
FindAll_AfterAddingTwoNewsItems_ReturnsACollectionWithCountOf2

我希望这会使方法更加清晰。

答案 1 :(得分:2)

您的FindAll_should_return_correct_news测试方法没有测试存储库,它正在测试自己。当您将其简化为真正的功能时,您可以看到这一点:

[Test]
public void FindAll_should_return_correct_news()
{
   // Arrange
   List<News> newsList = new List<News>();
   newsList.Add(new News { Id = 1, Title = "Test Title 1" });
   newsList.Add(new News { Id = 2, Title = "Test Title 2" });

   // Act
   var actual = newsList;

   // Assert
   Assert.AreEqual(2, actual.Count());
}

正如您所看到的,您基本上要做的是创建一个列表,填写它并测试它是否实际包含您放入其中的记录数。

当您的存储库除了数据库交互之外什么也不做(因此没有应用程序逻辑)时,没有什么可以使用单元测试来测试。您可以通过编写存储库的集成测试来解决此问题。你基本上可以用这样的集成测试做的是在测试数据库中插入一些记录(虽然使用真实的数据库,而不是内存数据库)然后调用真实的存储库类来查看它是否从测试中获取预期的记录数据库。所有这些都应该在事务中执行,并在测试结束时回滚(这可以确保这些测试值得信赖)。

当您使用允许编写LINQ查询的O / RM工具时,您也可以尝试不同的方法。您可以伪造LINQ提供程序,如this article中所示。

答案 2 :(得分:1)

可能希望通过ayende阅读this post