编写全面的单元测试
乔纳森·艾伦 发表于2012 5 24
- 版块:开发
- 主题:.net, 单元测试,编程,测试驱动开发,测试
在那些声称熟悉单元测试“最佳实践”的人们中有一种常见的观点:在一个测试中你应该只写1个断言,最多2个。那些宣称这个理论的人几乎很少展示他们的单元测试并且那些单元测试确实只有一个断言。显然,如果你采纳了他们的建议,一个不重要的小方法也会需要一打儿的断言来保证质量。本文意在通过实例证明每个测试多个断言不仅是必须的而且是有益的。
让我们来考虑下一个非常典型的在数据绑定场景中很常见的Person对象:
测试FirstName 属性
我们想要测试的第一件事是设置对象的FirstName属性,一开始:
[TestMethod] public void Person_FirstName_Set() { varperson = new Person("Adam", "Smith"); person.FirstName = "Bob"; Assert.AreEqual("Bob", person.FirstName); }
然后我们想测试FirstName 属性的更改通知。
[TestMethod] public void Person_FirstName_Set_PropertyChanged() { var person = new Person("Adam", "Smith"); var eventAssert = new Granite.Testing.PropertyChangedEventAssert(person); person.FirstName = "Bob"; eventAssert.Expect("FirstName"); }
当我们运行这个测试方法时,我们将受到一条失败信息“期望属性名是‘FirstName’,但是却收到了‘IsChanged’”。显然,设置‘FirstName’属性改变了‘IsChanged’标志属性,所以我们需要考虑到这点,因此我们添加一些代码混合到这段代码中:
[TestMethod]
public void Person_FirstName_Set_PropertyChanged()
{
var person = new Person("Adam", "Smith");
var eventAssert = new Granite.Testing.PropertyChangedEventAssert(person);
person.FirstName = "Bob";
eventAssert.SkipEvent(); //this was IsChanged//这里是IsChanged
eventAssert.Expect("FirstName");
}
以上两个测试通过后,我们开始考虑当‘FirstName’改变后其他还有哪里需要改变。仔细检查API,两个属性‘IsChanged’和‘FullName’是需要改变的。
[TestMethod]
public void Person_FullName_Changed_By_Setting_FirstName()
{
var person = new Person("Adam", "Smith");
person.FirstName = "Bob";
Assert.AreEqual("Bob Smith", person.FullName);
}
[TestMethod]
public void Person_IsChanged_Changed_By_Setting_FirstName()
{
var person = new Person("Adam", "Smith");
person.FirstName = "Bob";
Assert.IsTrue(person.IsChanged);
}
并且当这些属性被改变时,我们也理所应当能捕获到他们的属性更改通知:
[TestMethod]
public void Person_IsChanged_Property_Change_Notification_By_Setting_FirstName()
{
var person = new Person("Adam", "Smith");
var eventAssert = new PropertyChangedEventAssert(person);
person.FirstName = "Bob";
eventAssert.Expect("IsChanged");
}
[TestMethod]
public void Person_FullName_Property_Change_Notification_By_Setting_FirstName()
{
var person = new Person("Adam", "Smith");
var eventAssert = new PropertyChangedEventAssert(person);
person.FirstName = "Bob";
eventAssert.SkipEvent(); //this was IsChanged
eventAssert.SkipEvent(); //this was FirstName
eventAssert.Expect("FullName");
}
我们下面的两个测试将检测‘HasError’属性和‘ErrorsChanged’事件。
[TestMethod]
public void Person_FirstName_Set_HasErrorsIsFalse()
{
var person = new Person("Adam", "Smith");
person.FirstName = "Bob";
Assert.IsFalse(person.HasErrors);
}
[TestMethod]
public void Person_FirstName_Set_ErrorsChanged_Did_Not_Fire()
{
var person = new Person("Adam", "Smith");
var errorsChangedAssert = new ErrorsChangedEventAssert(person);
person.FirstName = "Bob";
errorsChangedAssert.ExpectNothing();
}
到目前为止我们已经有了8个测试方法,这意味着我们已对所有‘FirstName’属性改变后应该改变的地方担负起了责任。但这并不意味着我们现在已经全部搞定了。我们也需要确保其他任何东西没有被意外的改变。