单元测试跨层边界

Unit Test Crossing Layer Boundaries

我有一个单元测试,我正在测试我的服务 class 在各种条件下将数据插入到我的存储库 class 中的正确位置。我的存储库 class 有三个 "storage" 位置,我的服务 class 有一个 public AddData 方法,它有条件地将数据添加到三个存储之一(是生产中不同的数据库表集)在存储库中。让我的单元测试查看服务的存储库以确保将数据添加到正确的位置是否可以?例如:

服务 class 如下所示:

class MyService
{
   private readonly IRepository Repository;       

   // Add data to a different place in the repository based on the value of the 'someSwitch' parameter
   public void AddData(MyDataObject data, int someSwitch)
   {   
      if (someSwitch >= 0 && someSwitch < 10)
      { 
         this.Repository.AddToStorageOne(data);
      }
      else if (someSwitch >= 10 && someSwitch < 20)
      {
         this.Repository.AddToStorageTwo(data);
      }
      else
      {
         this.Repository.AddToStorageThree(data);
      }
   }
}

单元测试如下所示:

[TestClass]
public class MyTests
{
   [TestMethod]
   private void AddData_SwitchValueOf15_ShouldAddToStorageTwo()
   {
      // Create the MyService class with an in-memory repository
      MockRepository repository = new MockRepository();
      MyService service = new MyService
      {
         Repository = repository
      };

      MyDataObject myDataObject = new MyDataObject();
      // Assign some data to myDataObject

      // This should insert myDataObject into StorageTwo
      service.AddData(myDataObject, 15);

      // Here I want to assert that the data got added to "StorageTwo" in the repository
   }
}

现在我想测试数据是否已插入存储库的 StorageTwo。当然,我很容易做

Assert.AreEqual(0, repository.StorageOne.Count);
Assert.AreEqual(1, repository.StorageTwo.Count);
Assert.AreEqual(0, repository.StorageThree.Count);

所以我的问题是,我的单元测试(正在测试服务方法)是否可以像这样查看服务的存储库?如果这样做是不好的做法,我应该如何检查数据是否插入到存储库中的正确位置?我的服务 class 只有一个 public GetData() 方法,它结合了来自 StorageOneStorageTwoStorageThree 的数据,因此服务 class 没有任何可以查看单个存储的 public 方法。

在单元测试中,你真的不应该超出你的 class 的边界。一种方法是模拟每个依赖项。一旦他们被嘲笑,您就可以验证您的 class 与外界的互动。

这也有助于减少仅为测试编写的代码。例如,您必须在场景中公开存储的 Count 以供测试。如果您盲目地信任 repo 来完成它的工作(因为它也应该进行单元测试)并且只是检查您是否正确调用它,则没有必要。

在您的特定情况下,您可以模拟 Repository 然后断言调用了正确的方法。作为额外的安全措施,您可以验证其他人不是。

对于最小起订量,它看起来像这样:

[TestClass]
public class MyTests
{
   [TestMethod]
   private void AddData_SwitchValueOf15_ShouldAddToStorageTwo()
   {
      // Mock the repository then add it to the service
      Mock<IRepository> mockRepository = new Mock<IRepository>();

      MyService service = new MyService
      {
         Repository = mockRepository 
      };

      MyDataObject myDataObject = new MyDataObject();
      // Assign some data to myDataObject

      // This should insert myDataObject into StorageTwo
      service.AddData(myDataObject, 15);

      // Check that the correct method was called once, with our parameter
      mockRepository.Verify(r => r.AddToStorageTwo(myDataObject), Times.Once());

      // Check that the other methods were never called, with any input
      mockRepository.Verify(r => r.AddToStorageOne(It.IsAny<MyDataObject>()), Times.Never());
      mockRepository.Verify(r => r.AddToStorageThree(It.IsAny<MyDataObject>()), Times.Never());
   }
}