10. 11. /*12. * 判断一个整数是否在(0, 200)区间内 13. * 返回值:true-否; false-是 14. */
15. bool isOutOfRange(int i); 16. 17. /*
18. * 判断三条边是否合法(即:判断三条边都在合法的范围内) 19. * 返回值:true-是; false-否 20. */
21. bool isLegal(int a, int b, int c); 22. 23. /*
24. * 判断两条边之和是否大于第三边 25. * 返回值:true-是; false-否 26. */
27. bool isSumBiger(int a, int b, int c); 28. 29. /*
30. * 判断三条边是否能够组成三角形 31. * 返回值:true-是; false-否 32. */
33. bool isTriangle(int a, int b, int c); 34. 35. /*
8
36. * 判断两条边是否相等 37. * 返回值:true-是; false-否 38. */
39. bool isEquals(int a, int b); 40. 41. /*
42. * 求三角形有几条边相等 43. * 返回值:相等边的数量 44. */
45. int howManyEquals(int a, int b, int c); 46. 47. /*
48. * 判断是否满足两边平方之和是否等于第三边的平方 49. * 50. */
51. bool isPowerSumEquals(int a, int b, int c); 52. 53. /*
54. * 判断第一个数是否比第二个数大 55. */
56. bool isGreaterThan(int a, int b); 57. 58. /*
59. * 判断是否是直角三角形 60. * 61. */
62. bool isRightRriangle(int a, int b, int c); 63. 64. /*
9
65. * 判断三角形的类型,返回值: 66. * 1、不能组成三角形 67. * 2、等边三角形 68. * 3、等腰三角形 69. * 4、直角三角形 70. * 5、一般三角形 71. * 6、某些边不满足限制 72. */ 73. int triangleType(int a, int b, int c);
白盒测试实例之六——单元测试的步骤
白盒测试与黑盒测试的过程和方法是有一些区别的。 单元测试的步骤: 1、 理解需求和设计 理解设计是很重要的,特别是要搞清楚被测试模块在整个软件中所处的位置,这对测试的内容将会有很大的影响。需要记住的一个原则就是:好的设计,各模块只负责完成自己的事情,层次与分工是很明确的。在单元测试的时候,可以不用测试不属于被测试模块所负责的功能,以减少测试用例的冗余,集成测试的时候会有机会测试到的。 举例: 1. /* 2. 3. * 判断三条边是否能够组成三角形 4. 5. * 返回值:true-是; false-否 6. 7. */ 8. 10
9. bool isTriangle(int a, int b, int c); 测试该函数的时候,只需要测试三条边(在合法的取值范围内的整数)是否能够满足两边之和是否大于第三边的功能,而不需要测试三条边是否在合法的范围(0, 200)之间的整数,因为调用该函数之前,一定要先通过下面函数的检查,要是检查不通过,就不会执行isTriangle函数。 1. /* 2. 3. * 判断三条边是否合法(即:判断三条边都在合法的范围内) 4. 5. * 返回值:true-是; false-否 6. 7. */ 8. 9. bool isLegal(int a, int b, int c); 所以,单元测试主要是关注本单元的内部逻辑,而不用关注整个业务的逻辑,因为会有别的模块去完成相关的功能
白盒测试实例之七——单元测试的尝试
以测试isOutOfRange函数为例,首先知道该函数在整个软件架构中处于最底层(叶子),所以对它进行测试并不需要写桩模块,只需要写驱动模块。要注意的问题是:对于测试结果是否通过测试不要使用printf方式打印被测试函数的返回结果值,否则就需要人工去检查结果了。 使用边界值的方法可以得到5个测试用例,写的驱动模块代码如下: TestTriangle.cpp: 1. /* 2. * Copyright (c) 2008, 胡添发(hutianfa@163.com) 3. * 4. * 单元测试与集成测试 11
5. * 6. */
7. #include \"Triangle.h\" 8. /*
9. * 测试isOutOfRange函数,使用边界值的方法(0,1,5,199,200) 10. * 11. */
12. void testIsOutOfRange_try() 13. {
14. if(isOutOfRange(0) == true) 15. {
16. printf(\"pass!\\n\"); 17. } 18. else 19. {
20. printf(\"fail!\\n\"); 21. } 22.
23. if(isOutOfRange(1) == false) 24. {
25. printf(\"pass!\\n\"); 26. } 27. else 28. {
29. printf(\"fail!\\n\"); 30. } 31. 32. } 33.
12
34. 35. void main() 36. { 37. testIsOutOfRange_try(); 38. } 小知识:做单元测试的时候,一般不直接在main函数中写所有的测试代码,否则的话,main函数将会非常庞大。正确的做法:针对每个函数分别创建一个或若干个(函数比较复杂时)测试函数,测试函数的名称习惯以test开头。
写到这里发现重复的代码太多了,而且如果测试用例数量很多的话,对于测试结果的检查也将是很大的工作量。在测试有错误的时候,这样的单元测试结果也很难获得更多关于错误的信息。
解决问题的途径可以采用cppUnit单元测试框架。不过这里为了让学生能够对单元测试和单元测试框架有进一步的理解,我决定自己写一个类似cppUnit的简单的测试框架。
白盒测试实例之八——构建自己的单元测试框架(上)
在上一讲“单元测试的尝试”里我们遇到了几个问题: 1、代码重复的问题太多 2、测试结果需要人工去检查 3、对测试的总体信息也无从得知 本讲将构建一个简单的单元测试框架来解决以上的问题: 1、代码重复的问题太多 这个问题很容易解决,只需要把判断预期结果和实际结果的逻辑提取到某个函数中即可。从整个代码来看,有两种类型的结果的函数: (1)返回布尔型 (2)返回整数 因此,需要两个类型的判断预期结果和实际结果是否相符的函数: 1. /* 2. * 判断是否取值为真 13
3. */
4. void assertTrue(char *msg, bool actual) 5. { 6. if(actual) 7. { 8. printf(\".\"); 9. } 10. else 11. {
12. printf(\"F\"); 13. } 14. } 15. 16. /*
17. * 判断预期结果和实际结果是否相符 18. */
19. void assertEquals(char *msg, int expect, int actual) 20. {
21. if(expect == actual) 22. { 23. printf(\".\"); 24. } 25. else 26. {
27. printf(\"F\"); 28. } 29. }
小知识:XUnit系列的框架的习惯使用assert*的命名来定义判断函数,对于通过的测试习惯打印一个“.”号,而对于失败的测试习惯打印一个“F”。 2、测试结果需要人工去检查
14
对于测试结果不要使用printf方式打印被测试函数的返回结果值就可以避免这个问题。 3、对测试的总体信息也无从得知
除了问题1的解决办法里使用“.”表示测试通过和“F”表示测试失败可以提高对测试结果的信息的直观性之外,做单元测试的人还希望能够得到以下的信息: (1)执行的测试用例总数、通过的数量和失败的数量 (2)测试执行的时间
(3)如果测试用例执行失败了,希望知道是哪个测试用例失败,从而去分析失败的原因。
白盒测试实例之九——构建自己的单元测试框架(下)
完整的源代码如下: 1、UnitTest.h 1. /* 2. * Copyright (c) 2008, 胡添发 3. * 4. * 简单的单元测试框架 5. * 6. */ 7. 8. #include 9. #include 10. #include 11. #include 12. 13. /* 14. * VC中没有sleep函数,自己写一个 15. * wait单位是毫秒 16. */ 17. extern void sleep(clock_t wait); 18. 1519. 20. /* 21. * 判断是否取值为真 22. */ 23. void assertTrue(char *msg, bool actual); 24. 25. /* 26. * 判断预期结果和实际结果是否相符 27. */ 28. void assertEquals(char *msg, int expect, int actual); 29. 30. /* 31. * 初始化测试,开始计时 32. */ 33. void init(); 34. 35. /* 36. * 结束测试,结束计时,打印报告 37. */ 38. void end();
白盒测试实例之十——集成测试的概念
测一、桩模块和驱动模块(以C语言为例):
很多人对桩模块和驱动模块的概念会搞不清楚,下面先介绍这两个概念: 模块结构实例图:
16
假设现在项目组把任务分给了7个人,每个人负责实现一个模块。你负责的是B模块,你很优秀,第一个完成了编码工作,现在需要开展单元测试工作,先分析结构图: 1、由于B模块不是最顶层模块,所以它一定不包含main函数(A模块包含main函数),也就不能独立运行。
2、B模块调用了D模块和E模块,而目前D模块和E模块都还没有开发好,那么想让B模块通过编译器的编译也是不可能的。 那么怎样才能测试B模块呢?需要做:
1、写两个模块Sd和Se分别代替D模块和E模块(函数名、返回值、传递的参数相同),这样B模块就可以通过编译了。Sd模块和Se模块就是桩模块。
2、写一个模块Da用来代替A模块,里面包含main函数,可以在main函数中调用B模块,让B模块运行起来。Da模块就是驱动模块。 知识点:
桩模块的使命除了使得程序能够编译通过之外,还需要模拟返回被代替的模块的各种可能返回值(什么时候返回什么值需要根据测试用例的情况来决定)。
驱动模块的使命就是根据测试用例的设计去调用被测试模块,并且判断被测试模块的返回值是否与测试用例的预期结果相符。 二、集成测试策略: 1、 非增式集成测试
各个单元模块经过单元测试之后,一次性组装成完整的系统。 优点:集成过程很简单。
缺点:出现集成问题时,查找问题比较麻烦,而且测试容易遗漏。 范例:
17
2、 增式集成测试 (1)自顶向下 A、 纵向优先
从最顶层开始测试,需要写桩模块。测试的顺序:从跟节点开始,每次顺着某枝干到该枝干的叶子节点添加一个节点到已测试好的子系统中,接着再加入另一枝干的节点,直到所有节点集成到系统中。 B、 横向优先
跟纵向优先的区别在于:每次并不是顺着枝干走到叶子,而是逐一加入它的直属子节点。 纵向优先的范例:
18
(2)自底向上
每次从叶子节点开始测试,测试过的节点摘掉,然后把树上的叶子节点摘下来加入到已经测试好的子系统之中。优点:不需要写桩模块,但需要写驱动模块。 范例:
19