單元測試的兩種方式


在單元測試中,可通過兩種方式來驗證代碼是否正確地工作。一種是基於結果狀態的測試,一種是基於交互行為的測試。

測試結果與測試行為之間有什么區別呢?

基於結果狀態的測試,也就意味着我們需要驗證被測試代碼需要返回正確的結果。

 1     [TestMethod]
 2     public void TestSortNumberResult()
 3     {
 4       IShellSorter<int> shellSorter = new ShellSorter<int>();
 5       IBubbleSorter<int> bubbleSorter = new BubbleSorter<int>();
 6 
 7       NumberSorter numberSorter = new NumberSorter(shellSorter, bubbleSorter);
 8       int[] numbers = new int[] { 3, 1, 2 };
 9       numberSorter.Sort(numbers);
10 
11       // 驗證返回值是否已經被正確排序。
12       // 只要返回值正確即可,並不關心使用了哪個算法。
13       CollectionAssert.AreEqual(new int[] { 1, 2, 3 }, numbers);
14     }

基於交互行為的測試,也就意味着我們需要驗證被測試代碼是否正確合理地調用了某些方法。

 1     [TestMethod]
 2     public void TestUseCorrectSortingAlgorithm()
 3     {
 4       IShellSorter<int> mockShellSorter = Substitute.For<IShellSorter<int>>();
 5       IBubbleSorter<int> mockBubbleSorter = Substitute.For<IBubbleSorter<int>>();
 6 
 7       NumberSorter numberSorter = new NumberSorter(mockShellSorter, mockBubbleSorter);
 8       int[] numbers = new int[] { 3, 1, 2 };
 9       numberSorter.Sort(numbers);
10 
11       // 驗證排序器是否使用冒泡排序算法。
12       // 如果排序器未使用冒泡排序算法,或者使用了該算法但傳遞了錯誤的參數,則驗證失敗。
13       mockBubbleSorter.Received().Sort(Arg.Is<int[]>(numbers));
14     }

第二種測試方法可能會得出較好的代碼覆蓋率,但它卻沒有告訴我們排序結果是否正確,而只是確認調用了 bubbleSorter.Sort() 方法。所以交互行為測試並不能證明代碼可以正確工作。這也就是在大多數情況下,我們需要測試結果和狀態,而不是測試交互和行為。

通常來說,如果程序的正確性不能僅僅靠程序的輸出結果來決定,而還需要判斷結果是怎么產生的,在這種條件下,我們就需要對交互和行為進行測試。在上面的示例中,你可能想在得到正確測試結果的前提下,額外的再測試下交互行為,因為可能確認正確地使用了某種算法非常重要,例如某些算法在給定條件下運行速度更快,否則的話測試交互行為的意義並不大。

通常在什么條件下需要對交互行為進行測試呢?

這里給出兩種較適合的場景:

  • 假設被測試代碼需要調用了一個方法,但可能由於其被調用的次數不同,或者被調用的順序不同,而導致產生了不同的結果,或者出現了其他類似時間延遲、多線程死鎖等副作用。例如該方法負責發送郵件,我們需要確認只調用了一次郵件發送函數。或者例如該方法的不同調用順序會產生不同的線程鎖控制,導致死鎖。在類似這些情況下,測試交互行為可以有效地幫助你確認方法調用是否正確。
  • 假設我們在測試一個UI程序,其中已經通過抽象將UI渲染部分與UI邏輯部分隔離,可以考慮是某種MVC或MVVM模式。那么在我們測試 Controller 或 ViewModel 層時,如果有的話,可能只關心 View 上的哪些方法被調用了,而並不關系具體該方法內部是如何渲染的,所以此處測試與 View 的交互就比較合適。類似的,對於 Model 層也一樣。

完整代碼

  1   [TestClass]
  2   public class UnitTestTwoWays
  3   {
  4     public interface IShellSorter<T> 
  5       where T : IComparable
  6     {
  7       void Sort(T[] list);
  8     }
  9 
 10     public interface IBubbleSorter<T> 
 11       where T : IComparable
 12     {
 13       void Sort(T[] list);
 14     }
 15 
 16     public class ShellSorter<T> : IShellSorter<T> 
 17       where T : IComparable
 18     {
 19       public void Sort(T[] list)
 20       {
 21         int inc;
 22 
 23         for (inc = 1; inc <= list.Length / 9; inc = 3 * inc + 1) ;
 24 
 25         for (; inc > 0; inc /= 3)
 26         {
 27           for (int i = inc + 1; i <= list.Length; i += inc)
 28           {
 29             T t = list[i - 1];
 30             int j = i;
 31 
 32             while ((j > inc) && (list[j - inc - 1].CompareTo(t) > 0))
 33             {
 34               list[j - 1] = list[j - inc - 1];
 35               j -= inc;
 36             }
 37 
 38             list[j - 1] = t;
 39           }
 40         }
 41       }
 42     }
 43 
 44     public class BubbleSorter<T> : IBubbleSorter<T> 
 45       where T : IComparable
 46     {
 47       public void Sort(T[] list)
 48       {
 49         int i, j;
 50         bool done = false;
 51 
 52         j = 1;
 53         while ((j < list.Length) && (!done))
 54         {
 55           done = true;
 56 
 57           for (i = 0; i < list.Length - j; i++)
 58           {
 59             if (list[i].CompareTo(list[i + 1]) > 0)
 60             {
 61               done = false;
 62               T t = list[i];
 63               list[i] = list[i + 1];
 64               list[i + 1] = t;
 65             }
 66           }
 67 
 68           j++;
 69         }
 70       }
 71     }
 72 
 73     public interface INumberSorter
 74     {
 75       void Sort(int[] numbers);
 76     }
 77 
 78     public class NumberSorter : INumberSorter
 79     {
 80       private IShellSorter<int> _shellSorter;
 81       private IBubbleSorter<int> _bubbleSorter;
 82 
 83       public NumberSorter(
 84         IShellSorter<int> shellSorter, 
 85         IBubbleSorter<int> bubbleSorter)
 86       {
 87         _shellSorter = shellSorter;
 88         _bubbleSorter = bubbleSorter;
 89       }
 90 
 91       public void Sort(int[] numbers)
 92       {
 93         _bubbleSorter.Sort(numbers);
 94       }
 95     }
 96 
 97     [TestMethod]
 98     public void TestSortNumberResult()
 99     {
100       IShellSorter<int> shellSorter = new ShellSorter<int>();
101       IBubbleSorter<int> bubbleSorter = new BubbleSorter<int>();
102 
103       NumberSorter numberSorter = new NumberSorter(shellSorter, bubbleSorter);
104       int[] numbers = new int[] { 3, 1, 2 };
105       numberSorter.Sort(numbers);
106 
107       // 驗證返回值是否已經被正確排序。
108       // 只要返回值正確即可,並不關心使用了哪個算法。
109       CollectionAssert.AreEqual(new int[] { 1, 2, 3 }, numbers);
110     }
111 
112     [TestMethod]
113     public void TestUseCorrectSortingAlgorithm()
114     {
115       IShellSorter<int> mockShellSorter = Substitute.For<IShellSorter<int>>();
116       IBubbleSorter<int> mockBubbleSorter = Substitute.For<IBubbleSorter<int>>();
117 
118       NumberSorter numberSorter = new NumberSorter(mockShellSorter, mockBubbleSorter);
119       int[] numbers = new int[] { 3, 1, 2 };
120       numberSorter.Sort(numbers);
121 
122       // 驗證排序器是否使用冒泡排序算法。
123       // 如果排序器未使用冒泡排序算法,或者使用了該算法但傳遞了錯誤的參數,則驗證失敗。
124       mockBubbleSorter.Received().Sort(Arg.Is<int[]>(numbers));
125     }
126   }
View Code

關於單元測試 mocking 技術,請參考《NSubstitute完全手冊》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM