【问题标题】:Using DI for Unit Testing使用 DI 进行单元测试
【发布时间】:2017-01-13 22:41:52
【问题描述】:

我了解将 DI 用于依赖项并模拟它们以进行单元测试。但是,当我正在单元测试的当前功能有多个实现时,如何将它们注入到单元测试中。

例如:快速排序或合并排序

public class TestSort
{
  ISort sort = null;
    [TestInitialize]
    public void Setup()
    {
        sort = new MergeSort(); // or any implementation which need to be injected on setup
    }

【问题讨论】:

  • 参数化测试可能是一种选择,但请阅读 here 了解使用 MsTest 并不容易。
  • @kayess 这很有趣

标签: unit-testing dependency-injection


【解决方案1】:

如果你想测试你的“排序”方法,你应该对每个排序算法进行单独的单元测试。例如

[TestMethod]
public void MergeSort_SortUnderorderedList_ShouldSortListCorrectly()
{
   // Arrange
   ISort sort = new MergeSort();

   // Act
   int[] sortedList = sort.Sort(new int[] { 4, 2, 18, 5, 19 });

   // Assert
   Assert.AreEqual(2, sortedList[0]);
   Assert.AreEqual(4, sortedList[1]);
   Assert.AreEqual(5, sortedList[2]);
   Assert.AreEqual(18, sortedList[3]);
   Assert.AreEqual(19, sortedList[4]);      
}

[TestMethod]
public void QuickSort_SortUnderorderedList_ShouldSortListCorrectly()
{
   // Arrange
   ISort sort = new QuickSort();

   // Act
   int[] sortedList = sort.Sort(new int[] { 4, 2, 18, 5, 19 });

   // Assert
   Assert.AreEqual(2, sortedList[0]);
   Assert.AreEqual(4, sortedList[1]);
   Assert.AreEqual(5, sortedList[2]);
   Assert.AreEqual(18, sortedList[3]);
   Assert.AreEqual(19, sortedList[4]);   
}

当您为一个注入排序算法的类编写测试时,您不应该测试排序算法在该测试中是否正常工作。相反,您应该注入一个排序算法模拟并测试 Sort() 方法是否被调用(但不在该测试中测试排序算法的正确结果)。

本示例使用 Moq 进行模拟

public class MyClass
{
   private readonly ISort sortingAlgorithm;

   public MyClass(ISort sortingAlgorithm)
   {
      if (sortingAlgorithm == null)
      {
         throw new ArgumentNullException("sortingAlgorithm");
      }
      this.sortingAlgorithm = sortingAlgorithm;
   }

   public void DoSomethingThatRequiresSorting(int[] list)
   {
      int[] sortedList = this.sortingAlgorithm.Sort(list);

      // Do stuff with sortedList
   }
}

[TestClass]
public class MyClassTests
{
   [TestMethod]
   public void DoSomethingThatRequiresSorting_SomeCondition_ExpectedResult()
   {
      // Arrange - I assume that you need to use the result of Sort() in the 
      // method that you're testing, so the Setup method causes sortingMock 
      // to return the specified list when Sort() is called
      ISort sortingMock = new Mock<ISort>();
      sortingMock.Setup(e => e.Sort().Returns(new int[] { 2, 5, 6, 9 }));
      MyClass myClass = new MyClass(sortingMock.Object);

      // Act 
      myClass.DoSomethingThatRequiresSorting(new int[] { 5, 9, 2, 6 });

      // Assert - check that the Sort() method was called
      myClass.Verify(e => e.Sort(It.IsAny<int[]>));
   }
}

【讨论】:

  • 你注意到OP使用的是MsTest而不是NUnit吗??
  • @kayess NUnit 和我的回答有什么关系?
  • @ben 你的答案的第一部分是我需要消除的。并且 Mock 肯定会用于依赖项,我很清楚这一点。
  • @Pacchy 所以你想用一个单元测试方法来测试几种不同的排序算法?就个人而言,我会为每个排序算法编写单独的单元测试,否则测试逻辑会变得有点模糊。参数化测试更多地用于指定输入值和预期值,但您可以使用参数化测试来指定整数标识符,然后基于该标识符创建 ISort 实现。看这篇文章blogs.msdn.microsoft.com/vstsqualitytools/2009/09/05/…
  • @Pacchy 或者你可以创建一个SortFactory,使用一个方法来创建你所有的ISort 实现,然后在那个方法上循环你的测试。但同样,我不建议这样做。单元测试应该尽可能容易阅读,并且有条件地创建不同的排序算法会混淆测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-20
  • 2015-12-12
  • 1970-01-01
  • 1970-01-01
  • 2021-01-10
相关资源
最近更新 更多