系统—模块—子模块,子模块中不可分割的程序单元的测试,单元的粒度根据实际情况可能是 类或方法等。
面向对象编程中,最小单元就是方法。
单元测试目的是在集成测试和功能测试之前对系统可测试单元进行逐一检查和验证。
Automatic自动化
单元测试应该是全自动执行,测试用例通常会被频繁地触发执行。
单元测试不允许使用System.out人工验证,而必须使用断言来验证
Independent独立性
用例之间不允许相互调用,也不允许执行次序的先后依赖
如下testMethod2需要调用testMethod2会导致运行效率降低
@Test
public void testMethod1(){
}
@Test
public void testMethod2(){
testMethod1();
}
主流测试框架中,JUnit的用例执行顺序是无序的
TestNG支持测试用例的顺序执行(默认测试类用例按照字典升序执行,也可以通过XML或注解Priority方式来配置执行顺序)
Repeatable可重复性
不受外界影响,比如单元测试通常被放到持续集成中,每次有代码提交,单元测试都会被触发。
为了保证被测试模块的交付质量,需要符合BCDE原则
Mock
弥补一些不具备的因素
最简单的Mock方式是硬编码
最优雅的是使用配置文件
最佳方式是使用Mock框架,例如JMockit/EasyMock/JMock等
粗粒度
类覆盖:类中只要有方法或变量被测试用例调用或执行到
方法覆盖:测试用例执行过程中,某个方法被调用了。
细粒度
①、行覆盖:也称语句覆盖,度量可执行语句是否被执行到。
公式:执行到语句的行数/总的可执行语句行数
public class ConverageSampleMethods{
public Boolean testMethod(int a,int b,int c){
boolean result = false;
if(a==1 && b==2 || c==3){
result = true;
}
return result;
}
}
以上方法有5个可执行语句和3个入参
@Test
@DisplayName("line converage sample test")
void testLineConverageSample(){
CoverageSampleMethods coverageSampleMethods = newCoverageSampleMethods();
Assertions.assertTrue(coverageSampleMethods.testMethod( 1 , 2, 0));
}
以上测试用例的行覆盖率是 100% ,但是在执行过程中 c==3 的条件判断根本没有被执行到, a!=I 并且 c!=3 的情况难道不应该测试一下吗?由此可见,行覆盖的覆盖强度并不高,但由于容易计算,因此在主流的覆盖率工具中,它依然是一个十分常见的参考指标。
②、分支覆盖:也称为判定覆盖,用来度量程序中每 个判定分支是否都被执行到。
公式:代码中被执行到的分支数/所有分支的总数
③、条件判定覆盖:要求设计足够的测试用例,能够让判定中每个条件的所有可能情况至少被执行一次 同时每个判定本身的所有可能结果也至少执行一次。
@ParameterizedTest//定义一个参数化测试
@DisplayName("Condition Decision coverage sample test result true")
@CsvSource({
"0,2,3",
"1,0,3",
})//通过定义一个Stirng数组来定义多次运行时的参数列表,
void testConditionDecisionCoverageTrue(int a,int b,int c){
CoverageSampleMethods coverageSampleMethods = newCoverageSampleMethods() ;
Assertions assertTrue(coverageSampleMethods.testMethod(a, b, c)) ;
}
@DisplayName("Condition Decisior coverage sample test result false")
void testConditionDecisionConverageFalse(){
CoverageSampleMethods coverageSampleMethods = newCoverageSampleMethods() ;
Assertions assertTrue(coverageSampleMethods.testMethod(0, 0, 0));
}
④、条件组合覆盖:是指判定中所有条件的各种组合情况都出现至少一次。
@ParameterizeTest
@DisplayName("Multiple Condition Converage sample test result true")
@CsvSource({
"1,2,3",
"1,2,0",
"1,0,3",
"0,2,3",
"0,0,3",
})
void testMultipleConditionConverageSampleTrue(int a,int b,int c){
CoverageSampleMethods coverageSampleMethods= new CoverageSampleMethods();
Assertions.assertTrue(coverageSampleMethods.testMethod(a,b,c)) ;
}
@ParameterizeTest
@DisplayName("Multiple Condition Converage sample test result false")
@CsvSource({
"1,0,0",
"0,0,0",
"0,2,0",
})
void testMultipleConditionConverageSampleTrue(int a,int b,int c){
CoverageSampleMethods coverageSampleMethods= new CoverageSampleMethods();
Assertions.assertTrue(coverageSampleMethods.testMethod(a,b,c)) ;
}
这组测试用例同时满足了( a= !, b2, c3 )为( true, true, true )、( true,true, false )、( true, false, true )、( true, false, false )、( false, true, true )、( false,true, false )、( false, false, true )、( false, false , false )这 种情况。对于一个包含了 个条件的判定 至少需要 个测试用例才可以。虽然这种覆盖足够严谨,但无疑给编写测试用例增加了指数级的工作量。
⑤、路径覆盖:要求能够测试到程序中所有可能的路径
在 testMethod 方法中,可能的路径有 a1,b2,c==3 b!=2 c! =3 a! ,c !=1 c! =3种。当存在“||”时 如果第一个条件已经为 true不再计算后边表达式的值。而当存在“&& 时,如果第一个条件已经为 false同样不再计算后边表达式的值。满足路径覆盖的测试用例如下
@ParameterizeTest
@DisplayName("Path coverage sample test result true")
@CsvSource({
"1,2,0",
"1,0,3",
"0,0,3",
})
void testMultipleConditionConverageSampleTrue(int a,int b,int c){
CoverageSampleMethods coverageSampleMethods= new CoverageSampleMethods();
Assertions.assertTrue(coverageSampleMethods.testMethod