针对状态的间接测试

定义  基于状态的测试(也称状态验证),是指在方法执行之后,通过检查被测系统及其协作者(依赖项)的状态来检测该方法是否正确工作。

让我们来看一个基于状态测试的简单例子,它使用LogAnalyzer类,然而,我们不能简单地调用一个方法就完成测试。代码清单2.3给出了该类的代码。

代码清单2.3  通过调用IsValidLogFileName来测试属性的值

  
    
  1. public class LogAnalyzer  
  2. {  
  3.     private bool wasLastFileNameValid;  
  4.  
  5.     public bool WasLastFileNameValid  
  6.     {  
  7.         get { return wasLastFileNameValid; }  
  8.         set { wasLastFileNameValid = value; }  
  9.     }  
  10.  
  11.     public bool IsValidLogFileName(string fileName)  
  12.     {  
  13.         if (!fileName.ToLower().EndsWith(".slf"))  
  14.         {  
  15.             wasLastFileNameValid = false;  
  16.             return false;  
  17.         }  
  18.  
  19.         //存储状态的结果用于以后验证  
  20.         wasLastFileNameValid = true;  
  21.         return true;  
  22.     }  
  23. }  

从代码中可以看出,LogAnalyzer记住了验证检查的最终结果。因为逻辑上取决于先有其他方法被调用,所以我们要测试此功能,不能只是简单地写一个测试,用它来取得一个方法的返回值,我们必须使用其他手段来检测逻辑是否有问题。

首先,必须确定要测试的逻辑。是在新的属性wasLastFileNameValid中吗?不是的,是在IsValidLogFileName方法中,所以我们的测试名称必须以这个方法开头。代码清单2.4给出了一个简单测试,看其结果是否已记住。

代码清单2.4  通过调用方法并检查其属性值来测试一个类

  
    
  1. [Test]  
  2. public void IsValidLogFileName_ValidName_RemembersTrue()  
  3. {  
  4.     LogAnalyzer log = new LogAnalyzer();  
  5.  
  6.     log.IsValidLogFileName("somefile.slf");  
  7.       
  8.     Assert.IsTrue(log.WasLastFileNameValid);  
  9. }  

请注意,我们是在被测代码以外的地方做断言来测试IsValidLogFileName方法功能的。

代码清单2.5给出了另一个例子(我们将在第3章中再次用到)。在该例子中,查看一个内置的内存计算器功能。(参考本书的范例代码Calculator.cs和CalculatorTests.cs。)

代码清单2.5  Add()方法和Sum()方法

  
    
  1. public class Calculator  
  2. {  
  3.     private int sum = 0;  
  4.     public void Add(int number)  
  5.     {  
  6.         sum += number;  
  7.     }  
  8.     public int Sum()  
  9.     {  
  10.         int temp = sum; sum = 0;  
  11.         return temp;  
  12.     }  
  13. }  

Calculator类的工作原理类似于大家了解并喜欢的袖珍计算器。输入一个数字,然后按+,输入另外一个数字,然后再按+,诸如此类。输入结束时,按=即可得到当前的总和。

从哪里开始测试Sum()方法呢?应该坚持从最简单的测试开始,比如测试Sum()默认返回0。代码清单2.6给出了以上做法。

代码清单2.6  针对Calculator的Sum()方法的最简单测试

  
    
  1. [Test]  
  2. public void Sum_NoAddCalls_DefaultsToZero()  
  3. {  
  4.     Calculator calc = new Calculator();  
  5.  
  6.     int lastSum = calc.Sum();  
  7.  
  8.     Assert.AreEqual(0,lastSum);  
  9. }  

我们不能在调用Add()方法之前写其他测试,所以我们下一个测试必须调用Add()并对Sum()返回的数值作断言。代码清单2.7给出了附有新测试方法的测试类。

代码清单2.7  两个测试,其中第二个调用了Add()方法

  
    
  1. [SetUp]  
  2. public void Setup()  
  3. {  
  4.     calc = new Calculator();  
  5. }  
  6.  
  7. [Test]  
  8. public void Sum_NoAddCalls_DefaultsToZero()  
  9. {  
  10.     int lastSum = calc.Sum();  
  11.     Assert.AreEqual(0,lastSum);  
  12. }  
  13.  
  14. [Test]  
  15. public void Add_CalledOnce_SavesNumberForSum()  
  16. {  
  17.     calc.Add(1);  
  18.     int lastSum = calc.Sum();  
  19.     Assert.AreEqual(1,lastSum);  
  20. }  

请注意,这里的测试在[SetUp]相关的方法中初始化Calculator对象。这是个不错的想法,因为这样可以节省写测试的时间,使得代码更 少,而且确保用同样的方法来初始化Calculator。同样,也有更好的测试维护性,如果改变Calculator的构造函数,只需在一个地方改变初始 化代码,而无需在每个测试中都更改new调用。

到目前为止,一切都很好。但是如果被测方法依赖于外部资源又会怎样呢?比如依赖于文件系统、数据库、Web服务或者任何我们很难控制的其他东西。这正是我们开始新建测试桩对象(stub)、伪对象(fake)和模拟对象(mock)的时候,我们将在接下来几章中讨论到。

你可能感兴趣的:(测试)