如何编写综合的单元测试方案.docx

上传人:b****8 文档编号:9873712 上传时间:2023-05-21 格式:DOCX 页数:19 大小:118.73KB
下载 相关 举报
如何编写综合的单元测试方案.docx_第1页
第1页 / 共19页
如何编写综合的单元测试方案.docx_第2页
第2页 / 共19页
如何编写综合的单元测试方案.docx_第3页
第3页 / 共19页
如何编写综合的单元测试方案.docx_第4页
第4页 / 共19页
如何编写综合的单元测试方案.docx_第5页
第5页 / 共19页
如何编写综合的单元测试方案.docx_第6页
第6页 / 共19页
如何编写综合的单元测试方案.docx_第7页
第7页 / 共19页
如何编写综合的单元测试方案.docx_第8页
第8页 / 共19页
如何编写综合的单元测试方案.docx_第9页
第9页 / 共19页
如何编写综合的单元测试方案.docx_第10页
第10页 / 共19页
如何编写综合的单元测试方案.docx_第11页
第11页 / 共19页
如何编写综合的单元测试方案.docx_第12页
第12页 / 共19页
如何编写综合的单元测试方案.docx_第13页
第13页 / 共19页
如何编写综合的单元测试方案.docx_第14页
第14页 / 共19页
如何编写综合的单元测试方案.docx_第15页
第15页 / 共19页
如何编写综合的单元测试方案.docx_第16页
第16页 / 共19页
如何编写综合的单元测试方案.docx_第17页
第17页 / 共19页
如何编写综合的单元测试方案.docx_第18页
第18页 / 共19页
如何编写综合的单元测试方案.docx_第19页
第19页 / 共19页
亲,该文档总共19页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

如何编写综合的单元测试方案.docx

《如何编写综合的单元测试方案.docx》由会员分享,可在线阅读,更多相关《如何编写综合的单元测试方案.docx(19页珍藏版)》请在冰点文库上搜索。

如何编写综合的单元测试方案.docx

如何编写综合的单元测试方案

每个用例编写一到二个断言是单元测试最佳实践的常见内容.那些这么认为的是极少和只展示一个单元测试的人。

因此如果你采纳他们的建议,为一个很小的运算你都需要大量的单元测试去保证质量。

这篇文章意图通过例子展示,一个测试用例多个断言是有必要和有价值的。

  Person这个对象在数据绑定场景中经常出现,我们来看下。

  测试FirstName

  第一个来测试FirstName这个属性的设置,开始如下:

  [TestMethod]

1

2

3

4

5

6javakeyword">publicvoidPerson_FirstName_Set()

{

varperson=newPerson("Adam","Smith");

person.FirstName="Bob";

Assert.AreEqual("Bob",person.FirstName);

}

  接下来我们来测试FirstName的改变通知。

  [TestMethod]

1

2

3

4

5

6

7publicvoidPerson_FirstName_Set_PropertyChanged()

{

varperson=newPerson("Adam","Smith");

vareventAssert=newGranite.Testing.PropertyChangedEventAssert(person);

person.FirstName="Bob";

eventAssert.Expect("FirstName");

}

当我们执行这个测试时,会得到一个失败提示信息“期望的属性名‘FirstName’,但接收到的是’IsChanged’”。

显然,设置FirstName的属性触发了“IsChanged”标记,我们需要把它考虑在内。

因此我们把它加入:

  [TestMethod]

1

2

3

4

5

6

7

8publicvoidPerson_FirstName_Set_PropertyChanged()

{

varperson=newPerson("Adam","Smith");

vareventAssert=newGranite.Testing.PropertyChangedEventAssert(person);

person.FirstName="Bob";

eventAssert.SkipEvent();//thiswasIsChanged

eventAssert.Expect("FirstName");

}

  鉴于以上两个测试,我们考虑当FirstName被修改时还有其他什么属性会改变。

查看API,IsChanged和FullName属性会变化。

  [TestMethod]

1

2

3

4

5

6publicvoidPerson_FullName_Changed_By_Setting_FirstName()

{

varperson=newPerson("Adam","Smith");

person.FirstName="Bob";

Assert.AreEqual("BobSmith",person.FullName);

}

  [TestMethod]

1

2

3

4

5

6publicvoidPerson_IsChanged_Changed_By_Setting_FirstName()

{

varperson=newPerson("Adam","Smith");

person.FirstName="Bob";

Assert.IsTrue(person.IsChanged);

}

  当然,如果这些属性改变了,我们需要获取到属性改变通知:

  [TestMethod]

1

2

3

4

5

6

7publicvoidPerson_IsChanged_Property_Change_Notification_By_Setting_FirstName()

{

varperson=newPerson("Adam","Smith");

vareventAssert=newPropertyChangedEventAssert(person);

person.FirstName="Bob";

eventAssert.Expect("IsChanged");

}

  [TestMethod]

1

2

3

4

5

6

7

8

9publicvoidPerson_FullName_Property_Change_Notification_By_Setting_FirstName()

{

varperson=newPerson("Adam","Smith");

vareventAssert=newPropertyChangedEventAssert(person);

person.FirstName="Bob";

eventAssert.SkipEvent();//thiswasIsChanged

eventAssert.SkipEvent();//thiswasFirstName

eventAssert.Expect("FullName");

}

接下来两个测试针对HasErrors这个属性和ErrorsChanged事件。

  [TestMethod]

1

2

3

4

5

6publicvoidPerson_FirstName_Set_HasErrorsIsFalse()

{

varperson=newPerson("Adam","Smith");

person.FirstName="Bob";

Assert.IsFalse(person.HasErrors);

}

  [TestMethod]

1

2

3

4

5

6

7publicvoidPerson_FirstName_Set_ErrorsChanged_Did_Not_Fire()

{

varperson=newPerson("Adam","Smith");

varerrorsChangedAssert=newErrorsChangedEventAssert(person);

person.FirstName="Bob";

errorsChangedAssert.ExpectNothing();

}

  目前我们有8个测试了,这意味着当我们修改FirstName的属性值,我们要考虑会发生改变的每件事。

但是这不算完。

我们还需要确保没有别的会被意外改变。

理论上说,这意味着更多的断言和相当数量的测试,但是,接下来我们采用取巧的方法,用ChangeAssert方法来替代HasErrors测试。

  [TestMethod]

1

2

3

4

5

6

7publicvoidPerson_FirstName_Set_Nothing_Unexpected_Changed()

{

varperson=newPerson("Adam","Smith");

varchangeAssert=newChangeAssert(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个测试,它们都有同样的设置和运算。

唯一的不同是我们写的断言。

在业界这个被称为一个代码味道。

事实上,根据维基百科所列的这里应该有两个代码味道:

  ●Duplicatedcode

  ●重复的代码

  ●Excessivelylongidentifiers

  ●过长的标识符

  我们可以通过将断言合并到一个测试来轻松地消除这两个代码味道:

  [TestMethod]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16publicvoidPerson_FirstName_Set()

{

varperson=newPerson("Adam","Smith");

vareventAssert=newPropertyChangedEventAssert(person);

varerrorsChangedAssert=newErrorsChangedEventAssert(person);

varchangeAssert=newChangeAssert(person);

person.FirstName="Bob";

Assert.AreEqual("Bob",person.FirstName,"FirstNamesetterfailed");

Assert.AreEqual("BobSmith",person.FullName,"FullNamenotupdatedwithFirstNamechanged");

Assert.IsTrue(person.IsChanged,"IsChangedflagwasnotsetwhenFirstNamechanged");

eventAssert.Expect("IsChanged");

eventAssert.Expect("FirstName");

eventAssert.Expect("FullName");

errorsChangedAssert.ExpectNothing("ExpectednoErrorsChangedevents");

changeAssert.AssertOnlyChangesAre("FirstName","FullName","IsChanged");

}

知道什么导致测试失败很重要,因此我们在断言里添加失败的信息提示。

  单元测试和代码重用

  回看那27个测试用例,我们可以断定设置FirstName为null或者空串应该也需求同样的测试。

因此我们可以扩展成:

  [TestMethod]

1

2

3

4publicvoidPerson_FirstName_Set_Empty()

{

Person_FirstName_Set_Invalid(String.Empty);

}

  [TestMethod]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24publicvoidPerson_FirstName_Set_Null()

{

Person_FirstName_Set_Invalid(null);

}

publicvoidPerson_FirstName_Set_Invalid(stringfirstName)

{

varperson=newPerson("Adam","Smith");

vareventAssert=newPropertyChangedEventAssert(person);

varerrorsChangedAssert=newErrorsChangedEventAssert(person);

varchangeAssert=newChangeAssert(person);

Assert.IsFalse(person.IsChanged,"Testsetupfailed,IsChangedisnotfalse");

Assert.AreEqual("Adam",person.FirstName,"Testsetupfailed,FirstNameisnotAdam");

Assert.AreEqual("Smith",person.LastName,"Testsetupfailed,LastNameisnotSmith");

person.FirstName=firstName;

Assert.AreEqual(firstName,person.FirstName,"FirstNamesetterfailed");

Assert.AreEqual("Smith",person.FullName,"FullNamenotupdatedwithFirstNamechanged");

Assert.IsTrue(person.IsChanged,"IsChangedflagwasnotsetwhenFirstNamechanged");

eventAssert.Expect("IsChanged");

eventAssert.Expect("FirstName");

eventAssert.Expect("FullName");

Assert.IsTrue(person.HasErrors,"HasErrorsshouldhaveremainedfalse");

errorsChangedAssert.ExpectCountEquals(1,"ExpectedanErrorsChangedevent");

changeAssert.AssertOnlyChangesAre("FirstName","FullName","IsChanged","HasErrors");

}

可以发现Person_FirstName_Set和Person_FirstName_Set_Invalid的差异很小,我们可以进一步试着通用化:

  [TestMethod]

1

2

3

4publicvoidPerson_FirstName_Set_Valid()

{

Person_FirstName_Set("Bob",false);

}

  [TestMethod]

1

2

3

4publicvoidPerson_FirstName_Set_Empty()

{

Person_FirstName_Set(String.Empty,true);

}

  [TestMethod]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32publicvoidPerson_FirstName_Set_Null()

{

Person_FirstName_Set(null,true);

}

publicvoidPerson_FirstName_Set(stringfirstName,boolshouldHaveErrors)

{

varperson=newPerson("Adam","Smith");

vareventAssert=newPropertyChangedEventAssert(person);

varerrorsChangedAssert=newErrorsChangedEventAssert(person);

varchangeAssert=newChangeAssert(person);

Assert.IsFalse(person.IsChanged,"Testsetupfailed,IsChangedisnotfalse");

Assert.AreEqual("Adam",person.FirstName,"Testsetupfailed,FirstNameisnotAdam");

Assert.AreEqual("Smith",person.LastName,"Testsetupfailed,LastNameisnotSmith");

person.FirstName=firstName;

Assert.AreEqual(firstName,person.FirstName,"FirstNamesetterfailed");

Assert.AreEqual((firstName+"Smith").Trim(),person.FullName,"FullNamenotupdatedwithFirstNamechanged");

Assert.AreEqual(true,person.IsChanged,"IsChangedflagwasnotsetwhenFirstNamechanged");

eventAssert.Expect("IsChanged");

eventAssert.Expect("FirstName");

eventAssert.Expect("FullName");

if(shouldHaveErrors)

{

Assert.IsTrue(person.HasErrors,"HasErrorsshouldhaveremainedfalse");

errorsChangedAssert.ExpectCountEquals(1,"ExpectedanErrorsChangedevent");

changeAssert.AssertOnlyChangesAre("FirstName","FullName","IsChanged","HasErrors");

}

else

{

errorsChangedAssert.ExpectNothing("ExpectednoErrorsChangedevents");

changeAssert.AssertOnlyChangesAre("FirstName","FullName","IsChanged");

}

}

在测试代码变得令人迷惑之前,我们可以把它通用化什么程度,这里绝对有个限制。

但是一个有意义的测试名称,并给每个断言配一个好的描述可以让你的测试更加容易让人理解。

  控制变量

  目前所有的断言都只考虑到了测试用例的输出。

他们假设每个Person对象初始状态已知,然后从此出发进行其他操作。

但是如果我们想让测试更具科学性,必须确保我们能控制变量。

或者换句话说,我们需要保证,一切在掌握之中。

  请看下面一组断言:

1

2

3

4Assert.IsFalse(person.HasErrors,"Testsetupfailed,HasErrorsisnotfalse");

Assert.IsFalse(person.IsChanged,"Testsetupfailed,IsChangedisnotfalse");

Assert.AreEqual("Adam",person.FirstName,"Testsetupfailed,FirstNameisnotAdam");

Assert.AreEqual("Smith",person.LastName,"Testsetupfailed,LastNameisnotSmith");

  由于我们不想在每个测试的开始重复这些断言,我们可以选择把他们移到一个工厂方法中,这样我们可以保证总是拿到一个干净的对象。

这个同样适用于重用这些设置去测试其他属性的测试用例。

  [TestMethod]

1

2

3

4publicvoidPerson_FirstName_Set()

{

varperson=GetAdamSmith();

...

  表格式的测试

  之所以走到这一步,是因为“测试方法”的数量跟测试的完善程度没有关系。

它们只是组织和执行测试用例一种比较方便的方式。

  另一个组织大量测试用例的方法是表格驱动测试法。

不能执行单个测试,但是仅用一行代码就可以增加新的测试用例。

表格式测试里的表格可以来源于XML的文件,数据库表,写死在数组里或者只是使用同一个函数用不同的值反复调用。

一些框架如MBTest甚至可以让你用属性给出测试用例,但是为了让例子轻便,我们还是坚持保持最低的共同部分。

  [TestMethod]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24publicvoidPerson_FullName_Tests()

{

Person_FullName_Test("Bob","Jones","BobJones");

Person_FullName_Test("Bob","Jones","BobJones");

Person_FullName_Test("Bob","Jones","BobJ

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 初中教育 > 语文

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2