定义 基于状态的测试(也称状态验证),是指在方法执行之后,通过检查被测系统及其协作者(依赖项)的状态来检测该方法是否正确工作。
让我们来看一个基于状态测试的简单例子,它使用LogAnalyzer类,然而,我们不能简单地调用一个方法就完成测试。代码清单2.3给出了该类的代码。
代码清单2.3 通过调用IsValidLogFileName来测试属性的值
- public class LogAnalyzer
- {
- private bool wasLastFileNameValid;
- public bool WasLastFileNameValid
- {
- get { return wasLastFileNameValid; }
- set { wasLastFileNameValid = value; }
- }
- public bool IsValidLogFileName(string fileName)
- {
- if (!fileName.ToLower().EndsWith(".slf"))
- {
- wasLastFileNameValid = false;
- return false;
- }
- //存储状态的结果用于以后验证
- wasLastFileNameValid = true;
- return true;
- }
- }
从代码中可以看出,LogAnalyzer记住了验证检查的最终结果。因为逻辑上取决于先有其他方法被调用,所以我们要测试此功能,不能只是简单地写一个测试,用它来取得一个方法的返回值,我们必须使用其他手段来检测逻辑是否有问题。
首先,必须确定要测试的逻辑。是在新的属性wasLastFileNameValid中吗?不是的,是在IsValidLogFileName方法中,所以我们的测试名称必须以这个方法开头。代码清单2.4给出了一个简单测试,看其结果是否已记住。
代码清单2.4 通过调用方法并检查其属性值来测试一个类
- [Test]
- public void IsValidLogFileName_ValidName_RemembersTrue()
- {
- LogAnalyzer log = new LogAnalyzer();
- log.IsValidLogFileName("somefile.slf");
- Assert.IsTrue(log.WasLastFileNameValid);
- }
请注意,我们是在被测代码以外的地方做断言来测试IsValidLogFileName方法功能的。
代码清单2.5给出了另一个例子(我们将在第3章中再次用到)。在该例子中,查看一个内置的内存计算器功能。(参考本书的范例代码Calculator.cs和CalculatorTests.cs。)
代码清单2.5 Add()方法和Sum()方法
- public class Calculator
- {
- private int sum = 0;
- public void Add(int number)
- {
- sum += number;
- }
- public int Sum()
- {
- int temp = sum; sum = 0;
- return temp;
- }
- }
Calculator类的工作原理类似于大家了解并喜欢的袖珍计算器。输入一个数字,然后按+,输入另外一个数字,然后再按+,诸如此类。输入结束时,按=即可得到当前的总和。
从哪里开始测试Sum()方法呢?应该坚持从最简单的测试开始,比如测试Sum()默认返回0。代码清单2.6给出了以上做法。
代码清单2.6 针对Calculator的Sum()方法的最简单测试
- [Test]
- public void Sum_NoAddCalls_DefaultsToZero()
- {
- Calculator calc = new Calculator();
- int lastSum = calc.Sum();
- Assert.AreEqual(0,lastSum);
- }
我们不能在调用Add()方法之前写其他测试,所以我们下一个测试必须调用Add()并对Sum()返回的数值作断言。代码清单2.7给出了附有新测试方法的测试类。
代码清单2.7 两个测试,其中第二个调用了Add()方法
- [SetUp]
- public void Setup()
- {
- calc = new Calculator();
- }
- [Test]
- public void Sum_NoAddCalls_DefaultsToZero()
- {
- int lastSum = calc.Sum();
- Assert.AreEqual(0,lastSum);
- }
- [Test]
- public void Add_CalledOnce_SavesNumberForSum()
- {
- calc.Add(1);
- int lastSum = calc.Sum();
- Assert.AreEqual(1,lastSum);
- }
请注意,这里的测试在[SetUp]相关的方法中初始化Calculator对象。这是个不错的想法,因为这样可以节省写测试的时间,使得代码更 少,而且确保用同样的方法来初始化Calculator。同样,也有更好的测试维护性,如果改变Calculator的构造函数,只需在一个地方改变初始 化代码,而无需在每个测试中都更改new调用。
到目前为止,一切都很好。但是如果被测方法依赖于外部资源又会怎样呢?比如依赖于文件系统、数据库、Web服务或者任何我们很难控制的其他东西。这正是我们开始新建测试桩对象(stub)、伪对象(fake)和模拟对象(mock)的时候,我们将在接下来几章中讨论到。