




已阅读5页,还剩10页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
每个用例编写一到二个断言是单元测试最佳实践的常见内容.那些这么认为的是极少和只展示一个单元测试的人。因此如果你采纳他们的建议,为一个很小的运算你都需要大量的单元测试去保证质量。这篇文章意图通过例子展示,一个测试用例多个断言是有必要和有价值的。 Person这个对象在数据绑定场景中经常出现,我们来看下。 测试FirstName 第一个来测试FirstName这个属性的设置,开始如下: TestMethod 123456 java keywordpublic void Person_FirstName_Set() varperson = new Person(Adam, Smith); person.FirstName = Bob; Assert.AreEqual(Bob, person.FirstName); 接下来我们来测试FirstName的改变通知。 TestMethod 1234567 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 12345678 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 eventAssert.Expect(FirstName); 鉴于以上两个测试,我们考虑当FirstName被修改时还有其他什么属性会改变。查看API,IsChanged和FullName属性会变化。 TestMethod 123456 public void Person_FullName_Changed_By_Setting_FirstName() var person = new Person(Adam, Smith); person.FirstName = Bob; Assert.AreEqual(Bob Smith, person.FullName); TestMethod123456 public void Person_IsChanged_Changed_By_Setting_FirstName() var person = new Person(Adam, Smith); person.FirstName = Bob; Assert.IsTrue(person.IsChanged); 当然,如果这些属性改变了,我们需要获取到属性改变通知: TestMethod 1234567 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 123456789 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); 接下来两个测试针对HasErrors这个属性和ErrorsChanged事件。 TestMethod 123456 public void Person_FirstName_Set_HasErrorsIsFalse() var person = new Person(Adam, Smith); person.FirstName = Bob; Assert.IsFalse(person.HasErrors); TestMethod 1234567 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的属性值,我们要考虑会发生改变的每件事。但是这不算完。我们还需要确保没有别的会被意外改变。理论上说,这意味着更多的断言和相当数量的测试,但是,接下来我们采用取巧的方法,用ChangeAssert方法来替代HasErrors测试。 TestMethod 1234567 public void Person_FirstName_Set_Nothing_Unexpected_Changed() var person = new Person(Adam, Smith); var changeAssert = new ChangeAssert(person); person.FirstName = Bob; changeAssert.AssertOnlyChangesAre(FirstName, FullName, IsChanged); ChangeAssert简单地通过映射获取对象的状态,因此,稍后你可以断言到除了你指出的几个具体属性其他的没变。 恭喜,你完成了你的第一个测试用例。完成一个,还有很多很多等着。 为什么说是“一个”测试用例? 那8个测试只是完成了覆盖FirstName属性从“Adam”修改成“Bob”这一个场景,在其他的值没有在错误状态、LastName不为null或空的情况下。让我们看看测试用例的完整清单: 将FirstName值设置为“Adam” 将FirstName值设置为null 将FirstName 设为空串 在LastName值为null的情况下,执行case1-3 在LastName 为空串的情况下,执行case1-3 在FirstName值以null开头的情况下,执行case1-5 在FirstName值以空串开头的情况下,执行case1-5 目前我们看到了27个不同的场景。如果每个场景需要8个不同测试,仅仅为这一个属性,我们需要执行至多216个测试。根据这种思路,这是相当琐碎的一段代码。因此我们该怎么做呢? 测试也有代码味道 回看第一个测试用例的8个测试,它们都有同样的设置和运算。唯一的不同是我们写的断言。在业界这个被称为一个代码味道。事实上,根据维基百科所列的这里应该有两个代码味道: Duplicated code 重复的代码 Excessively long identifiers 过长的标识符 我们可以通过将断言合并到一个测试来轻松地消除这两个代码味道: TestMethod 12345678910111213141516 public void Person_FirstName_Set() var person = new Person(Adam, Smith); var eventAssert = new PropertyChangedEventAssert(person); var errorsChangedAssert = new ErrorsChangedEventAssert(person); var changeAssert = new ChangeAssert(person); person.FirstName = Bob; Assert.AreEqual(Bob, person.FirstName, FirstName setter failed); Assert.AreEqual(Bob Smith, person.FullName, FullName not updated with FirstName changed); Assert.IsTrue(person.IsChanged, IsChanged flag was not set when FirstName changed); eventAssert.Expect(IsChanged); eventAssert.Expect(FirstName); eventAssert.Expect(FullName); errorsChangedAssert.ExpectNothing(Expected no ErrorsChanged events); changeAssert.AssertOnlyChangesAre(FirstName, FullName, IsChanged); 知道什么导致测试失败很重要,因此我们在断言里添加失败的信息提示。 单元测试和代码重用 回看那27个测试用例,我们可以断定设置FirstName为null或者空串应该也需求同样的测试。因此我们可以扩展成: TestMethod 1234 public void Person_FirstName_Set_Empty() Person_FirstName_Set_Invalid(String.Empty); TestMethod 123456789101112131415161718192021222324 public void Person_FirstName_Set_Null() Person_FirstName_Set_Invalid(null); public void Person_FirstName_Set_Invalid(string firstName) var person = new Person(Adam, Smith); var eventAssert = new PropertyChangedEventAssert(person); var errorsChangedAssert = new ErrorsChangedEventAssert(person); var changeAssert = new ChangeAssert(person); Assert.IsFalse(person.IsChanged, Test setup failed, IsChanged is not false); Assert.AreEqual(Adam, person.FirstName, Test setup failed, FirstName is not Adam); Assert.AreEqual(Smith, person.LastName, Test setup failed, LastName is not Smith); person.FirstName = firstName; Assert.AreEqual(firstName , person.FirstName, FirstName setter failed); Assert.AreEqual(Smith, person.FullName, FullName not updated with FirstName changed); Assert.IsTrue(person.IsChanged, IsChanged flag was not set when FirstName changed); eventAssert.Expect(IsChanged); eventAssert.Expect(FirstName); eventAssert.Expect(FullName); Assert.IsTrue(person.HasErrors, HasErrors should have remained false); errorsChangedAssert.ExpectCountEquals(1, Expected an ErrorsChanged event); changeAssert.AssertOnlyChangesAre(FirstName, FullName, IsChanged, HasErrors); 可以发现Person_FirstName_Set和Person_FirstName_Set_Invalid的差异很小,我们可以进一步试着通用化: TestMethod 1234 public void Person_FirstName_Set_Valid() Person_FirstName_Set(Bob, false); TestMethod 1234 public void Person_FirstName_Set_Empty() Person_FirstName_Set(String.Empty, true); TestMethod 1234567891011121314151617181920212223242526272829303132 public void Person_FirstName_Set_Null() Person_FirstName_Set(null, true); public void Person_FirstName_Set(string firstName, bool shouldHaveErrors) var person = new Person(Adam, Smith); var eventAssert = new PropertyChangedEventAssert(person); var errorsChangedAssert = new ErrorsChangedEventAssert(person); var changeAssert = new ChangeAssert(person); Assert.IsFalse(person.IsChanged, Test setup failed, IsChanged is not false); Assert.AreEqual(Adam, person.FirstName, Test setup failed, FirstName is not Adam); Assert.AreEqual(Smith, person.LastName, Test setup failed, LastName is not Smith); person.FirstName = firstName; Assert.AreEqual(firstName, person.FirstName, FirstName setter failed); Assert.AreEqual(firstName + Smith).Trim(), person.FullName, FullName not updated with FirstName changed); Assert.AreEqual(true, person.IsChanged, IsChanged flag was not set when FirstName changed); eventAssert.Expect(IsChanged); eventAssert.Expect(FirstName); eventAssert.Expect(FullName); if (shouldHaveErrors) Assert.IsTrue(person.HasErrors, HasErrors should have remained false); errorsChangedAssert.ExpectCountEquals(1, Expected an ErrorsChanged event); changeAssert.AssertOnlyChangesAre(FirstName, FullName, IsChanged, HasErrors); else errorsChangedAssert.ExpectNothing(Expected no ErrorsChanged events); changeAssert.AssertOnlyChangesAre(FirstName, FullName, IsChanged); 在测试代码变得令人迷惑之前,我们可以把它通用化什么程度,这里绝对有个限制。但是一个有意义的测试名称,并给每个断言配一个好的描述可以让你的测试更加容易让人理解。 控制变量 目前所有的断言都只考虑到了测试用例的输出。他们假设每个Person对象初始状态已知,然后从此出发进行其他操作。但是如果我们想让测试更具科学性,必须确保我们能控制变量。或者换句话说,我们需要保证,一切在掌握之中。 请看下面一组断言: 1234 Assert.IsFalse(person.HasErrors, Test setup failed, HasErrors is not false); Assert.IsFalse(person.IsChanged, Test setup failed, IsChanged is not false); Assert.AreEqual(Adam, person.FirstName, Test setup failed, FirstName is not Adam); Assert.AreEqual(Smith, person.LastName, Test setup failed, LastName is not Smith); 由于我们不想在每个测试的开始重复这些断言,我们可以选择把他们移到一个工厂方法中,这样我们可以保证总是拿到一个干净的对象。这个同样适用于重用这些设置去测试其他属性的测试用例。 TestMethod 1234 public void Person_FirstName_Set() var person = GetAdamSmith(); . 表格式的测试 之所以走到这一步,是因为“测试方法”的数量跟测试的完善程度没有关系。它们只是组织和执行测试用例一种比较方便的方式。 另一个组织大量测试用例的方法是表格驱动测试法。不能执行单个测试,但是仅用一行代码就可以增加新的测试用例。表格式测试里的表格可以来源于XML的文件,数据库表,写死在数组里或者只是使用同一个函数用不同的值反复调用。一些框架如MBTest甚至可以让你用属性给出测试用例,但是为了让例子轻便,我们还是坚持保持最低的共同部分。 TestMethod 123456789101112131415161718192021222324 public void Person_FullName_Tests() Person_FullName_Test(Bob, Jones, Bob Jones); Person_FullName_Test(Bob , Jones, Bob Jones); Person_FullName_Test( Bob, Jones, Bob Jones); Person_FullName_Test(Bob, Jones, Bob Jones); Person_FullName_Test(Bob, Jones , Bob Jones); Person_FullName_Test(null,
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024版购销合同协议书补充协议
- 2025年事业单位工勤技能-河北-河北计算机信息处理员五级初级历年参考题库含答案解析
- 2025年事业单位工勤技能-河北-河北城管监察员二级(技师)历年参考题库含答案解析
- 2025年事业单位工勤技能-江西-江西机械热加工五级(初级工)历年参考题库含答案解析(5套)
- 新解读《GB-T 36444-2018信息技术 开放系统互连 简化目录协议及服务》
- 2025年事业单位工勤技能-江苏-江苏检验员三级(高级工)历年参考题库含答案解析(5套)
- 2025年事业单位工勤技能-广东-广东水利机械运行维护工五级(初级工)历年参考题库含答案解析
- 2025年事业单位工勤技能-广东-广东家禽饲养员四级(中级工)历年参考题库典型考点含答案解析
- 2025年事业单位工勤技能-广东-广东兽医防治员一级(高级技师)历年参考题库含答案解析
- 2025年事业单位工勤技能-安徽-安徽计算机操作员一级(高级技师)历年参考题库典型考点含答案解析
- 公共场所卫生知识培训材料
- 证据目录范本
- 标准档案盒脊背(格式已设置好)
- GB/T 21475-2008造船指示灯颜色
- 园林绿化工高级技师知识考试题库(附含答案)
- 安医大生殖医学课件04胚胎的培养
- 可下载打印的公司章程
- 关于推荐评审高级工程师专业技术职务的推荐意见报告
- Q∕GDW 10356-2020 三相智能电能表型式规范
- 教研工作手册
- CINV化疗相关呕吐课件
评论
0/150
提交评论