目录
白盒测试主要是检查程序的内部结构、逻辑、循环和路径。白盒测试的常用用例设计方法有逻辑覆盖和基本路径测试。
根据覆盖测试的目标不同,逻辑覆盖又可分为语句覆盖、判断覆盖、条件覆盖、判断/条件覆盖、条件组合覆盖及路径覆盖。

图1 一个程序模块的例图
从图中可知,该程序模块有4条不同的路径、4个可执行语句。这4条路径可分别表示为:
L1(a→c→e)简化为ace;
L2(a→b→d)简化为abd;
L3(a→b→e)简化为abe;
L4(a→c→d)简化为acd;
4条路径应该满足的逻辑表达式分别为:
记M={(A>1)and(B=0)},N={(A=2)or(X>1)}
L1(a→c→e)= M and N
L2(a→b→d)= /M and /N
L3(a→b→e)= /M and N
L4(a→c→d)= M and /N
其中,“/M”表示M的反,“/N”表示N的反,或者将4条路径应该满足的逻辑表达式写为:
L1(a→c→e)= {(A>1)and(B=0)} and {(A=2)or(X/A>1)} ;
L2(a→b→d)= / {(A>1)and(B=0)} and / {(A=2)or(X>1)} ;
L3(a→b→e)=/ {(A>1)and(B=0)} and {(A=2)or(X>1)} ;
L4(a→c→d)= {(A>1)and(B=0)} and / {(A=2)or(X/A>1)};
其中,由符号“and”连接起来的语句是为了遍历这条路径各个输入变量应取值的范围,而由“or”划分了几组可选的取值。根据上面导出的逻辑表达式,就可以用来设计测试用例。
语句覆盖就是设计若干个测试用例,运行所测程序,使得每一条可执行语句至少执行一次。对上面的例子,所有的可执行语句都在路径L1上,所以选择路径L1来设计测试用例,就要覆盖所有的可执行语句。
测试用例设计的格式如下:
[输入的(A,B,X),输出的(A,B,X)]为覆盖路径:L1(a→c→e)= M and N,{(A>1)and(B=0)} and {(A=2)or(X/A>1)}。可设计出满足语句覆盖的测试用例是:
[(2,0,4),(2,0,3)],覆盖ace[L1]。
从每个执行语句都得到执行这一点来看,语句覆盖的方法似乎能能够比较全面地检验每个可执行语句,但实际上并非如此。
语句覆盖的不足之处是:假如该程序段中的两个逻辑运算有问题,例如,第一个判断中的逻辑运算符“&&”写成了“||”,或者第二个判断中的逻辑运算符“||”错写成了“&&”,利用上面的测试用例,仍然可覆盖所有4个可执行语句,这说明虽然做到了语句覆盖测试,但可能发现不了判断时逻辑运算中出现的错误。语句覆盖是最弱的逻辑覆盖准则。
判断覆盖就是设计若干个测试用例,运行所测程序,使得程序中每个判断的取TRUE分支和取FALSE分支至少经历一次。判断覆盖又称为分支覆盖。
根据定义,可分别选择路径L1和L2或者路径L3和L4设计测试用例。
如果选择路径L1和L2,则可得到满足要求的测试用例:
[(2,0,4),(2,0,3)],覆盖ace[L1]
[(1,1,1),(1,2,1)],覆盖abd[L2]
如果选择路径L3和L4,则可设计另一组测试用例:
[(2,1,1),(2,1,2)],覆盖abe[L3]
[(3,0,3),(3,0,1)],覆盖acd[L4]
可见,测试用例的选择不唯一。
判断覆盖的不足之处是:假如第二个判断中的条件X>1被错写成了X<1,利用上面的两组测试用例仍能得到同样的结果。
条件覆盖就是设计若干个测试用例,运行所测程序,使得程序中每个判断的每个条件的可能取值至少执行一次。因此,首先要对所有的条件加以标记。
对第一个判断:
对第二个判断:
根据这8个条件取值,可分别设计如下两组测试用例,如表1和表2所列。
表1 第一组
| 测试用例 | 通过路径 | 条件取值 | 覆盖分支 |
| [(1,0,3),(1,0,4)] | abe(L3) | F1,T2,F3,T4 | b,e |
| [(2,1,1),(2,1,2)] | abe(L3) | T1,F2,T3,F4 | b,e |
表2 第二组
| 测试用例 | 通过路径 | 条件取值 | 覆盖分支 |
| [(2,0,4),(2,0,3)] | ace(L1) | T1,T2,T3,T4 | c,e |
| [(1,0,1),(1,0,1)] | abd(L2) | F1,T2,F3,F4 | b,d |
| [(2,1,1),(2,1,2)] | abe(L3) | T1,F2,T3,,F4 | b,e |
由表1和表2可以看出,两组测试用例都满足了条件覆盖,即覆盖了所有的体哦阿健取值。条件覆盖的不足之处是:第一组测试用例不满足判断(分支)覆盖的要求。
判断-条件覆盖就是设计足够的测试用例,使得判断中每个条件的所有可能取值至少执行一次,同时每个判断的所有可能判断结果至少执行一次。也就是说,要求各个判断的所有可能的条件取值组合至少执行一次。
根据判断-条件覆盖的定义,只需设计下面两个测试用例来覆盖例子的8个条件取值以及4个判断分支,如表3所列。
表3 判断分支列表
| 测试用例 | 通过路径 | 条件取值 | 覆盖分支 |
| [(2,0,4),(2,0,3)] | ace(L1) | T1,T2,T3,T4 | c,e |
| [(2,1,1),(2,1,2)] | abd(L2) | F1,F2,F3,F4 | b,d |
判断-条件覆盖的不足之处是:从表面上看来,判断-条件覆盖测试了所有条件的取值,但实际上并非如此,而是某些条件掩盖了另一些条件(由于多重条件判定),例如,对条件表达式(A>1)AND(B=0)来说,若(A>1)的测试结果为FALSE,可以立即确定表达式的结果为FALSE,这时往往就不再测试(B=0)的取值了,因此,条件(B=0)就没有被检查。同样,对条件表达式(A=2)OR(X>1)来说,若(A=2)的测试结果为TRUE,就立即确定表达式的结果为TRUE,这时,条件(X>1)就没有被检查。
因此,采用判断-条件覆盖测试,逻辑表达式中的错误不一定能够检查的出来。
条件组合覆盖就是设计足够的测试用例,运行所测程序,使得每个判断的所有可能的条件取值组合至少执行一次。
针对上面的例子,先对各个判断的条件取值组合加以标记,如表4所列。
表4 判断条件取值组合表
| 组合编号 | 条件取值的组合 | 标记 | 说明 |
| ① | A>1,B=0 | T1,T2 | 属第一个判断的取TRUE分支 |
| ② | A>1,B!=0 | T1,F2 | 属第一个判断的取FLASE分支 |
| ③ | A<=1,B=0 | F1,T2 | 属第一个判断的取FLASE分支 |
| ④ | A<=1,B!=0 | F1,F2 | 属第一个判断的取FLASE分支 |
| ⑤ | A=2,X>1 | T3,T4 | 属第二个判断的取TRUE分支 |
| ⑥ | A=2,X<=1 | T3,F4 | 属第二个判断的取TRUE分支 |
| ⑦ | A!=2,X>A | F3,T4 | 属第二个判断的取TRUE分支 |
| ⑧ | A!=2,X<=1 | F3,F4 | 属第二个判断的取FLASE分支 |
对每个判断,要求所有可能的条件取值的组合都必须取到。每个判断各有两个条件,所以各有4个条件取值的组合。
注意:这里没有要求第一个判断的4个组合再与第二个判断的4个组合进行组合成16个组合。
设计4个测试用例,就可覆盖上面的8种条件组合,如表5所列。
表5 4个测试取值组合列表
| 测试用例 | 通过路径 | 覆盖条件 | 覆盖组合号 |
| [(2,0,4),(2,0,3)] | ace(L1) | T1,T2,T3,T4 | ①⑤ |
| [(2,1,1),(2,1,2)] | abe(L3) | T1,F2,T3,F4 | ②⑥ |
| [(1,0,3),(1,0,4)] | abe(L3) | F1,T2,F3,T4 | ③⑦ |
| [(1,1,1),(1,1,1)] | abd(L2) | F1,F2,F3,F4 | ④⑧ |
这些测试用例覆盖了所有条件的可能取值的组合,覆盖了所有判断的可取分支。条件组合覆盖的不足之处:没有覆盖路径L4,这样的测试还不完全。
路径覆盖就是设计足够的测试用例,覆盖程序中所有可能的路径。可设计下面的4个测试用例,覆盖全部4条路径。表6所列是对应实例程序的路径覆盖测试用例。
表6 设计4个测试用例覆盖全部4条路径
| 测试用例 | 通过路径 | 覆盖条件 | 覆盖组合号 |
| [(2,0,4),(2,0,3)] | ace(L1) | T1,T2,T3,T4 | ①⑤ |
| [(1,1,1),(1,1,1)] | abd(L2) | F1,F2,F3,F4 | ④⑧ |
| [(1,1,2),(1,1,3)] | abe(L3) | F1,F2,F3,T4 | ④⑦ |
| [(3,0,3),(3,0,1)] | acd(L4) | T1,T2,F3,F4 | ①⑧ |
路径覆盖的不足之处,没有全部覆盖判断的条件取值的组合,如②③⑥。
对该例子,采用逻辑覆盖方法中的任何一种都不能满足所有需求,如果每个方法都采用,又有测试用例的重复。如何用最少的测试用例满足最多的需求呢?在设计中,这是一个值得考虑和解决的问题。
对于本例,我们采用条件组合覆盖和路径覆盖两种方法设计测试用例,并进行优化,可得到如表7所列的测试用例。
表7 采用条件组合覆盖和路径覆盖设计测试用例表
| 测试用例 | 通过路径 | 覆盖条件 | 覆盖组合号 |
| [(2,0,4),(2,0,3)] | ace(L1) | T1,T2,T3,T4 | ①⑤ |
| [(1,1,1),(1,1,1)] | abd(L2) | F1,F2,F3,F4 | ④⑧ |
| [(1,1,2),(1,1,3)] | abe(L3) | F1,F2,F3,T4 | ④⑦ |
| [(3,0,3),(3,0,1)] | acd(L4) | T1,T2,F3,F4 | ①⑧ |
| [(2,0,4),(2,0,3)] | ace(L1) | T1,T2,T3,T4 | ①⑤ |
| [(2,1,1),(2,1,2)] | abe(L3) | T1,F2,T3,F4 | ②⑥ |
| [(1,0,3),(1,0,4)] | abe(L3) | F1,T2,F3,T4 | ③⑦ |
| [(1,1,1),(1,1,1)] | abd(L2) | F1,F2,F3,F4 | ④⑧ |
采用上面的8个测试用例,可满足所有逻辑覆盖测试。
这篇帖子到这里就结束了,最后,希望看这篇帖子的朋友能够有所收获。如果想以测试为长期发展职业目标,是需要时刻保持学习的,要使自己具备竞争力,无论你现在工作几年,只要行动起来,你就已经占优势了。祝大家2022年能升职加薪,没入职的就早日拿到心仪公司的offer,事事顺遂。
欢迎留言,或是关注我的专栏和我交流。
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test
我在pry中定义了一个函数:to_s,但我无法调用它。这个方法去哪里了,怎么调用?pry(main)>defto_spry(main)*'hello'pry(main)*endpry(main)>to_s=>"main"我的ruby版本是2.1.2看了一些答案和搜索后,我认为我得到了正确的答案:这个方法用在什么地方?在irb或pry中定义方法时,会转到Object.instance_methods[1]pry(main)>defto_s[1]pry(main)*'hello'[1]pry(main)*end=>:to_s[2]pry(main)>defhello[2]pry(main)
我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel