单测的思路

单测的定义

  • 单元测试(Unit Testing)是一种软件开发中的测试方法,它的主要目的是确保软件中的最小可测试单元(通常是函数、方法或类)在被单独测试和验证时能够按照预期工作。尽管单元测试有很多优点,如提高代码质量、减少Bug、简化调试过程等,但它也存在一些缺点:
    • 增加开发时间:如要求覆盖率到80%甚至90%,或者入参几十个难以构造,单测时间占比可能超过30%。
    • 需要维护:随着代码的改变,特别是大规模的重构,单元测试也需要相应地更新和维护,增加开发的负担。
    • 无法发现对其他类的影响:单元测试主要关注单个单元的行为,无法发现与多个单元交互或整个系统相关的问题。
  • 所以部分公司会要求写接口维度、场景维度的单测,覆盖率在50-60%,甚至不强制要求覆盖率。

方法的单测

推荐用更智能的squaretest生成单测模板后,手工调整。

几种生成工具的对比

  • diffblue
    • 优点:
      • 与IntelliJ IDEA集成良好,使用方便。
      • 支持多种编程语言和框架。
    • 缺点:
      • 商用版本收费较高,对于个人用户或小型团队可能不太友好。
      • 在处理某些特定写法或框架时可能不够灵活。
  • squaretest
    • 优点:
      • 生成测试用例,自动覆盖部分if分支,减轻测试负担。
    • 缺点:
      • 只有30天的免费试用期,之后需要付费使用。事实上点掉remind后可以继续使用。
      • 没有社区版支持,对于开源项目或个人用户可能不太友好。
  • EvoSuite
    • 优点:
      • 作为Maven插件使用,方便集成到Java项目中。
      • 支持生成多样化的测试用例,有助于发现潜在的缺陷。
    • 缺点:
      • 社区支持相对较少,遇到问题时可能难以得到及时帮助。
      • 配置和使用可能相对复杂,需要一定的学习成本。
      • 在处理某些特定场景或框架时可能不够灵活或有效。
  • TestMe
    • 优点:
      • 简单易用,适合初学者或小型项目使用。
    • 缺点:
      • 需要手动填充输入参数和逻辑,自动化程度较低。
      • 生成的测试用例可能不够全面或深入,需要额外补充和完善。

生成步骤

  1. 安装插件
    在这里插入图片描述

  2. 引入依赖

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <version>2.1.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.8.2</version>
    </dependency>
  1. 编写业务代码
@Service
public class TestServiceImpl implements TestService {
   

    @Resource
    private TestRepository testRepository;
    @Resource
    private TestThird testThird;

    @Override
    public void start(InputDTO inputDTO) {
   
        InputEntity entity = testRepository.select(inputDTO.getId());
        if (entity == null) {
   
            testRepository.insert(entity = new InputEntity());
        }
        testThird.callThird(entity);
    }
}
  1. 生成单测
    在这里插入图片描述在这里插入图片描述
  2. 单测生成结果
/**
 * squaretest
 */
class TestServiceImplTest {
   

    @Mock
    private TestRepository mockTestRepository;
    @Mock
    private TestThird mockTestThird;

    @InjectMocks
    private TestServiceImpl testServiceImplUnderTest;

    @BeforeEach
    void setUp() {
   
        initMocks(this);
    }

    @Test
    void testStart() {
   
        // Setup
        final InputDTO inputDTO = new InputDTO();
        inputDTO.setName("name");
        inputDTO.setId(0);
        final InputDetail inputDetail = new InputDetail();
        inputDetail.setName("name");
        inputDTO.setInputDetail(inputDetail);

        // Configure TestRepository.select(...).
        final InputEntity inputEntity = new InputEntity();
        inputEntity.setId(0);
        inputEntity.setName("name");
        when(mockTestRepository.select(0)).thenReturn(inputEntity);

        when(mockTestRepository.insert(any(InputEntity.class))).thenReturn(0);

        // Run the test
        testServiceImplUnderTest.start(inputDTO);

        // Verify the results
        verify(mockTestRepository).insert(any(InputEntity.class));
        verify(mockTestThird).callThird(any(InputEntity.class));
    }
}

/**
 * testme
 */
class TestServiceImplTestTestMe {
   

    @Mock
    TestRepository testRepository;
    @Mock
    TestThird testThird;
    @InjectMocks
    TestServiceImpl testServiceImpl;

    @BeforeEach
    void setUp() {
   
        MockitoAnnotations.initMocks(this);
    }

    @Test
    void testStart() {
   
        when(testRepository.select(anyInt())).thenReturn(new InputEntity());
        when(testRepository.insert(any())).thenReturn(Integer.valueOf(0));

        testServiceImpl.start(new InputDTO());
    }
}

接口的单测

mock外部依赖,启动容器,调用接口

  1. 编写外部依赖的mock类
@Service
public class TestThirdImpl implements TestThird {
   

    @Override
    public void callThird(InputEntity entity) {
   
        System.out.println("TestThirdImpl callThird");
    }
}
//mock
public class TestThirdMockImpl implements TestThird {
   

    public void callThird(InputEntity entity) {
   
        System.out.println("TestThirdMockImpl callThird");
    }
}

  1. 替换容器中的bean,mock外部依赖
@Configuration
public class MockConfig {
   

    @Bean
    public BeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor() {
   
        return new BeanDefinitionRegistryPostProcessor() {
   
            @Override
            public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
   
                //移除依赖的bean
                registry.removeBeanDefinition("testThirdImpl");
                //获取Mockbean的定义
                BeanDefinition beanDe = BeanDefinitionBuilder.rootBeanDefinition(TestThirdMockImpl.class).getBeanDefinition();
                //注册mockbean
                registry.registerBeanDefinition("testThirdImpl", beanDe);
            }

            @Override
            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   

            }
        };
    }
}
  1. test模块中启动容器,并调用入口方法
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class TestApplicationTest {
   

    @Resource
    private TestService testService;

    @Test
    public void start() {
   
        testService.start(new InputDTO());
    }

}

场景的单测

将接口单测组合

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class TestApplicationTest {
   

    @Resource
    private TestService testService;

    @Test
    public void start() {
   
        testService.start(new InputDTO());
        testService.end(new InputDTO());
    }

}

总结

  • 方法的单测:覆盖入参少、业务分支多的场景。
  • 接口、场景的单测:覆盖主干流程。

参考

相关推荐

  1. golang

    2024-02-15 09:22:02       35 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-15 09:22:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-15 09:22:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-15 09:22:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-15 09:22:02       20 阅读

热门阅读

  1. whisperspeech 英文TTS的实现

    2024-02-15 09:22:02       26 阅读
  2. Linux命令速查表:简洁高效,一表掌握

    2024-02-15 09:22:02       28 阅读
  3. RabbitMQ的高可用机制

    2024-02-15 09:22:02       31 阅读
  4. react 插槽

    2024-02-15 09:22:02       28 阅读
  5. 支持向量机SVM

    2024-02-15 09:22:02       30 阅读
  6. Spring Cloud Gateway:使用RestController动态更新路由

    2024-02-15 09:22:02       29 阅读
  7. 收藏:《不要和不懂组织的人讨论管理》

    2024-02-15 09:22:02       37 阅读
  8. re:从0开始的CSS之旅 16. 高度塌陷问题

    2024-02-15 09:22:02       27 阅读
  9. 设计模式——适配器模式

    2024-02-15 09:22:02       27 阅读