【问题标题】:Help/advice needed with unit testing repositories单元测试存储库所需的帮助/建议
【发布时间】:2010-12-09 13:31:56
【问题描述】:

我正在使用 .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 中失败?

抱歉所有问题,但它们是新手问题:)

希望有人能帮帮我。

【问题讨论】:

  • 我看对了吗? NewsRepository 本身也有同样的东西?
  • 看起来您的测试正在测试 Rhino Mocks。也就是说,您设置了一个模拟对象并断言它返回您告诉它在测试中返回的内容。根据您的描述,我认为您将拥有一个与数据库交互的数据层,这就是您将模拟来测试您的业务逻辑。
  • 你有 NewsRepository 实现了 INewsRepository,但作为构造函数也得到了另一个 INewsRepository。当然,如果它是 INewsRepository 的唯一实现,则永远无法实例化它。
  • 我有一个 NewsService 将调用这个 NewsRepository 实例,我也将对其进行测试。我想一次做一件事。请记住我是新手,我更喜欢代码示例,而不是批评。
  • 这不是批评,只是反馈;-)。

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


【解决方案1】:

正如 Steven 所说,您是 Asserting 反对上述代码中的 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 方法之外,并在方法开始时调用的辅助方法中执行。由于对 Setup 的调用将是不可见的,并且使模拟的初始化不清楚。

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

FindAll_should_return_correct_news
例如:
FindAll_AfterAddingTwoNewsItems_ReturnsACollectionWithCountOf2

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

【讨论】:

    【解决方案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 中所见。

    【讨论】:

    • 谢谢。我明白你的意思了。测试完成后,您是否有一些示例代码可以将其回滚?
    • 当我在集成测试中测试这个时,我需要测试服务层调用和存储库调用吗?我有一个调用此存储库的 FindAll 方法的服务层。
    • 您希望尽量减少集成测试的数量,因此我建议在测试服务层(使用单元测试)时对存储库进行存根,并编写直接调用存储库的集成测试。
    【解决方案3】:

    可能需要阅读 ayende 的 this post

    【讨论】:

      猜你喜欢
      • 2020-01-07
      • 2010-12-05
      • 2011-08-12
      • 1970-01-01
      • 2011-06-10
      • 1970-01-01
      • 2017-05-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多