简体   繁体   English

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

[英]Help/advice needed with unit testing repositories

I am using .NET 4, NUnit and Rhino mocks. 我正在使用.NET 4,NUnit和Rhino模拟。 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. 上面的代码编译但在NUnit GUI中关于构造函数值失败。 I can only assume it has to do with the INewsRepository paramter that I need to supply to NewsRepository. 我只能假设它与我需要提供给NewsRepository的INewsRepository参数有关。 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? 有人可以纠正我的单元测试,以便它将通过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? 另一个开发人员必须对我的存储库做什么才能使其在NUnit GUI中失败?

Sorry for all the questions, but they are newbie questions :) 对不起所有的问题,但他们是新手问题:)

I hope soomeone can help me out. 我希望soomeone可以帮助我。

As Steven has said, you're Assert ing against the Mock NewsRepository in the above code. 正如史蒂文所说的那样,你在上面的代码中对Mock NewsRepository进行了Assert

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 ; 您使用Mock NewsRepository来测试使用INewsRepository东西,在您的情况下,您提到了NewsService ; NewsService will use your mock of INewsRepository . NewsService将使用您的INewsRepository模拟。

If you search your solution for anything that uses INewsRepository.FindAll(), you will create a Mock Repository to test that code in isolation. 如果您在解决方案中搜索使用INewsRepository.FindAll()的任何内容,您将创建一个Mock Repository来单独测试该代码。

If you want to test something that calls your Service layer, you will need to mock NewsService . 如果您想测试调用Service层的内容,则需要模拟NewsService

Also, as Steven as said, there is no need for the NewsRepository to have a copy of itself injected by IoC, so: 此外,正如史蒂文所说, NewsRepository没有必要拥有由IoC注入的自身副本,因此:

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 . 一旦在FindAll()方法中有需要测试的功能,就可以模拟它们使用的对象

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. 作为一个伟大的Art Of Unit测试的风格点,模拟对象的初始化最好不在Setup方法中,而是在方法开始时调用的辅助方法中执行。 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 ". 作为另一种风格,从该书中,建议的单元测试命名约定是:“ MethodUnderTest_Scenario_ExpectedBehavior ”。 So, 所以,

FindAll_should_return_correct_news
could become, for example: 可能成为,例如:
FindAll_AfterAddingTwoNewsItems_ReturnsACollectionWithCountOf2

I hope this makes the approach clearer. 我希望这会使方法更清晰。

Your FindAll_should_return_correct_news test method is not testing the repository, it is testing itself. 您的FindAll_should_return_correct_news测试方法不测试存储库,它正在测试自己。 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. 当您使用允许编写LINQ查询的O / RM工具时,您也可以尝试不同的方法。 You can fake your LINQ provider, as you can see in this article . 您可以伪造LINQ提供程序,如本文所示

可能想通过ayende阅读这篇文章

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM