单元测试实施方案
1.关于单元测试
单元测试实施在软件生命周期中的早期,是最小的测试单元,开发人员可以独立的编写单元测试(隔离性),可以在早期就发现软件的问题并修复。
单元测试的三个阶段:
1. 按功能编写单元测试
2. 验证并修复
3. 完成单元测试
编写单元测试时需要注意的地方:
- 每个单元测试都应该相互独立,是最小的测试单元,只测试public API
- 需要测试的功能只被测试一次
- 分三类用例:正常用例,边界用例,错误用例
- 不依赖特定的环境(Jdbc、mq、redis、外部接口,使用mock)
- 具有可读性
- 和生产环境代码分开
- 及时的修复
相关文档:
1.https://www.testingxperts.com...
2. http://fluxens.com/unittestin...
2.单元测试要求
- 准则:1. Automatic(自动化)2.Independent(独立性)3.Repeatable(可重复)
- 结构:测试代码放在src/test目录下,各个模块分子包,测试类以Test结尾,方法以test开头。
- 编码:
1).BCDE原则:
B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
C:Correct,正确的输入,并得到预期的结果。
D:Design,与设计文档相结合,来编写单元测试。
E:Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得到预期的结果。2). 涉及到外部接口、第三方模块使用mock模拟
3).使用断言确认执行结果
- 维度:
1).接口功能性测试
接口功能的正确性,即保证接口能够被正常调用,并输出有效数据!
------------------> 是否被顺利调用
------------------> 参数是否符合预期2).局部数据结构测试
保证数据结构的正确性
------------------> 变量是否有初始值或在某场景下是否有默认值
------------------> 变量是否溢出3).边界条件测试
------------------> 变量无赋值(null)
------------------> 变量是数值或字符
------------------> 主要边界:最大值,最小值,无穷大
------------------> 溢出边界:在边界外面取值+/-1
------------------> 临近边界:在边界值之内取值+/-1
------------------> 字符串的边界,引用 "变量字符"的边界
------------------> 字符串的设置,空字符串
------------------> 字符串的应用长度测试
------------------> 空白集合
------------------> 目标集合的类型和应用边界
------------------> 集合的次序
------------------> 变量是规律的,测试无穷大的极限,无穷小的极限4).所有独立代码测试
保证每一句代码,所有分支都测试完成,主要包括代码覆盖率,异常处理通路测试
------------------> 语句覆盖率:每个语句都执行到了
------------------> 判定覆盖率:每个分支都执行到了
------------------> 条件覆盖率:每个条件都返回布尔
------------------> 路径覆盖率:每个路径都覆盖到了5).异常模块测试
后续处理模块测试:是否包闭当前异常或者对异常形成消化,是否影响结果!
- 目标:
语句覆盖>= 70%
分支覆盖 100%
函数覆盖 100%
行覆盖 >= 80%
3.测试方案
使用 spring-boot-starter-test
、 database-rider
、 H2
内存数据库
测试范围: WEB、DAL、BIZ
测试步骤:
1.准备数据、行为
- 测试目标模块
- 验证测试结果
测试示例
1).WEB
// 测试类
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("local")
@AutoConfigureMockMvc
public class ControllerTest {
//需要mock的bean
@MockBean
private Service service;
@Autowired
private MockMvc mockMvc;
@Test
public void testWork() throws Exception {
// 添加依赖类的行为
when(service.work()).thenReturn("work");
String resultStr = this.mockMvc.perform(MockMvcRequestBuilders.get("/work"))
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn().getResponse().getContentAsString();
Assertions.assertEquals("work", resultStr);
}
}
// controller
@RestController
public class Controller {
@Autowired
private Service service;
@GetMapping("/work")
public String work(){
return service.work();
}
}
接口测试:使用 MockMvc
测试web层Api, MockMvc
提供了丰富的方法用去发起http请求
准备数据和行为:web层依赖于service层 需要mock service,所以需要使用 @MockBean
注解标注service,在具体的测试方法里为service添加行为
注意:依赖的bean可以使用 MockBean
或 SpyBean
,SpyBean
和MockBean
是spring-boot-test
包所提供的两个注解,用于Spy或Mock Spring容器所管理的实例。而Spy与Mock的方式正好相反,spy默认所有方法均真实调用,Mock默认所有方法均调用mock的实现.两者都可以为类添加指定行为,依赖的类为外部接口、redis等使用 MockBean
,为内部实现是更具需求使用。
2).BIZ
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("local")
public class serviceTest {
// mock的类,可为其添加行为
@MockBean
private Dao dao;
// 待测试的类
@Autowired
private Service service;
@Test
public void testWork() {
// 为mock类添加行为
doReturn("work").when(dao).work();
Assertions.assertEquals("work", service.work());
}
}
与WEB类似, 直接调用待测试方法验证即可.
3)DAL
DAL测试依赖于H2内存数据库和 DBRider(为数据库测试提供数据打桩)
1加入依赖:
com.h2database
h2
1.4.195
test
com.github.database-rider
rider-spring
1.21.0
test
2添加H2配置(在测试的包目录中)
public class H2Config {
@Bean
public DataSource h2DataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:db/schema.sql")
.addScript("classpath:db/data.sql")
.build();
}
}
resources/db/schema.sql 文件存放一些建表语句
resources/db/data.sql 文件存放一些初始化数据语句
3测试代码
resources 目录下有setUp.yml
user:
- id: 1
name: "@realpestano"
- id: 2
name: "wy"
- id: 3
name: "wy1"
- id: 4
name: "wy2"
// 测试类
@RunWith(SpringRunner.class)
@SpringBootTest
@DBRider
@Import(H2Config.class)
public class DaoTest {
@Autowired
private Dao dao;
@DataSet(value = "setUp.yml", cleanAfter = true)
@Test
public void testQuery() {
Integer count = dao.queryCount();
Assertions.assertEquals(4, count);
}
}
// dao
@Repository
public class Dao {
@Autowired
private JdbcTemplate jdbcTemplate;
public Integer queryCount() {
return jdbcTemplate.queryForObject("select count(*) from user", Integer.class);
}
}
测试方法 @DBRider
开启DBRider 特性
测试方法上 @DataSet
用来指定测试需要的数据,setUp.yml 里记录了一些和实体类对于的数据。
通过内存数据库和DBRider 提供的特性进行DAL层测试。
相关资料:
DBRider 文档:https://github.com/database-r...
Mockito文档:https://javadoc.io/doc/org.mo...
SpringBoot test 文档:https://docs.spring.io/spring...