单元测试实施方案

单元测试实施方案

1.关于单元测试

​ 单元测试实施在软件生命周期中的早期,是最小的测试单元,开发人员可以独立的编写单元测试(隔离性),可以在早期就发现软件的问题并修复。

​ 单元测试的三个阶段:

 1. 按功能编写单元测试
 2. 验证并修复
 3. 完成单元测试

编写单元测试时需要注意的地方:

  1. 每个单元测试都应该相互独立,是最小的测试单元,只测试public API
  2. 需要测试的功能只被测试一次
  3. 分三类用例:正常用例,边界用例,错误用例
  4. 不依赖特定的环境(Jdbc、mq、redis、外部接口,使用mock)
  5. 具有可读性
  6. 和生产环境代码分开
  7. 及时的修复

相关文档:

1.https://www.testingxperts.com...

​ 2. http://fluxens.com/unittestin...

2.单元测试要求

  1. 准则:1. Automatic(自动化)2.Independent(独立性)3.Repeatable(可重复)
  2. 结构:测试代码放在src/test目录下,各个模块分子包,测试类以Test结尾,方法以test开头。
  3. 编码:

    1).BCDE原则:

    B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
    C:Correct,正确的输入,并得到预期的结果。
    D:Design,与设计文档相结合,来编写单元测试。
    E:Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得到预期的结果。

    2). 涉及到外部接口、第三方模块使用mock模拟

    3).使用断言确认执行结果

  4. 维度:

    1).接口功能性测试

    接口功能的正确性,即保证接口能够被正常调用,并输出有效数据!
    ------------------> 是否被顺利调用
    ------------------> 参数是否符合预期

    2).局部数据结构测试

    保证数据结构的正确性
    ------------------> 变量是否有初始值或在某场景下是否有默认值
    ------------------> 变量是否溢出

    3).边界条件测试

    ------------------> 变量无赋值(null)
    ------------------> 变量是数值或字符
    ------------------> 主要边界:最大值,最小值,无穷大
    ------------------> 溢出边界:在边界外面取值+/-1
    ------------------> 临近边界:在边界值之内取值+/-1
    ------------------> 字符串的边界,引用 "变量字符"的边界
    ------------------> 字符串的设置,空字符串
    ------------------> 字符串的应用长度测试
    ------------------> 空白集合
    ------------------> 目标集合的类型和应用边界
    ------------------> 集合的次序
    ------------------> 变量是规律的,测试无穷大的极限,无穷小的极限

    4).所有独立代码测试

    保证每一句代码,所有分支都测试完成,主要包括代码覆盖率,异常处理通路测试
    ------------------> 语句覆盖率:每个语句都执行到了
    ------------------> 判定覆盖率:每个分支都执行到了
    ------------------> 条件覆盖率:每个条件都返回布尔
    ------------------> 路径覆盖率:每个路径都覆盖到了

    5).异常模块测试

    后续处理模块测试:是否包闭当前异常或者对异常形成消化,是否影响结果!

  5. 目标:

    语句覆盖>= 70%

    分支覆盖 100%

    函数覆盖 100%

    行覆盖 >= 80%

3.测试方案

使用 spring-boot-starter-test database-riderH2 内存数据库

测试范围: WEB、DAL、BIZ

测试步骤:

1.准备数据、行为

  1. 测试目标模块
  2. 验证测试结果

测试示例

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可以使用 MockBeanSpyBean ,SpyBeanMockBeanspring-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...

你可能感兴趣的:(java,单元测试,spring)