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

发布于 2024-10-07 00:12:51 字数 1609 浏览 0 评论 0原文

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

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

我希望有人可以帮助我。

I am using .NET 4, NUnit and Rhino mocks. I want to unit test my news repository, but I am not sure of how to go about it. My news repository is what I will eventually be using to communicate to the database. I want to use it to test against fake/dummy data. Not sure if it is possible?? This is what I currently have:

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;
   }
}

My unit test looks like this:

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());
   }
}

In the above code I am not sure what I need to mock. The code above compiles but fails in the NUnit GUI about a contructor value. I can only assume it has to do with the INewsRepository paramter that I need to supply to NewsRepository. I don't know how to do this in the test. Can someone please rectify my unit test so that it will pass in the NUnit GUI? Can someone also provide some feedback on if I am implementing my repositories correctly?

Being a newbie to mocking, is there anything that I need to verify? When would I need to verify? What is its purpose? I have been working through a couple of source code projects and some use verify and some don't.

If the above test passes, what does this prove to me as developer? What does another developer have to do to my repository to make it fail in the NUnit GUI?

Sorry for all the questions, but they are newbie questions :)

I hope soomeone can help me out.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烟酉 2024-10-14 00:12:51

正如史蒂文所说,您在上面的代码中对 Mock NewsRepository 进行断言。

模拟的想法是隔离被测代码并创建伪造品来替换它们的依赖项

您使用 Mock NewsRepository 来测试使用 INewsRepository 的内容,在您的情况下,您提到 NewsServiceNewsService 将使用您的 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() 方法中有需要测试的功能,你可以模拟他们使用的对象

作为伟大

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

FindAll_should_return_ Correct_news
例如可以变成:
FindAll_AfterAddingTwoNewsItems_ReturnsACollectionWithCountOf2

我希望这能让该方法更加清晰。

As Steven has said, you're Asserting against the Mock NewsRepository in the above code.

The idea of mocking is to isolate the Code Under Test and to create fakes to replace their dependencies.

You use the Mock NewsRepository to test something that uses INewsRepository, in your case, you mention NewsService; NewsService will use your mock of INewsRepository.

If you search your solution for anything that uses INewsRepository.FindAll(), you will create a Mock Repository to test that code in isolation.

If you want to test something that calls your Service layer, you will need to mock NewsService.

Also, as Steven as said, there is no need for the NewsRepository to have a copy of itself injected by IoC, so:

public class NewsRepository : INewsRepository
{
   private readonly INewsRepository newsRepository;

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

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

should become:

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

Once you have functionality in your FindAll() method that needs testing, you can mock the objects that they use.

As a point of style from the great Art Of Unit Testing initialisation of mock objects is best left out of the Setup method and carried out in a helper method called at the start of the method. Since the call to Setup will be invisible and makes the initalisation of the mock unclear.

As another point of style, from that book, a suggested unit test naming convention is: "MethodUnderTest_Scenario_ExpectedBehavior".
So,

FindAll_should_return_correct_news
could become, for example:
FindAll_AfterAddingTwoNewsItems_ReturnsACollectionWithCountOf2

I hope this makes the approach clearer.

埋葬我深情 2024-10-14 00:12:51

您的 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 提供程序,如本文。

Your FindAll_should_return_correct_news test method is not testing the repository, it is testing itself. You can see this when you simplify it to what it really does:

[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());
}

As you can see, what you're basically doing is creating a list, filling it and testing if it actually contains the number of records that you put in it.

When your repository does nothing else than database interaction (so no application logic) there is nothing to test using a unit test. You can solve this problem by writing integration tests for the repositories. What you can basically do with such a integration test is insert some records in a test database (use a real database though, not an in-memory database) and then call the real repository class to see if it fetches the expected records from your test database. All should be executed within a transaction and rolled back at the end of the test (this ensures these tests keep trustworthy).

When you're using a O/RM tool that allows you to write LINQ queries, you could also try a different approach. You can fake your LINQ provider, as you can see in this article.

盗心人 2024-10-14 00:12:51

可能需要阅读 ayende 的这篇文章

Might want to read over this post by ayende

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文