Technical note

研发规约-jUnit测试

简介

工程必须编写Junit单元测试,只有通过所有测试并达到指定覆盖率之后,才允许构建。

初期不强制要求,视团队接受/排斥情况而定

约定

存放及命名

所有单元测试均放在src/test/java文件夹下,命名为类名+Test(s).java,即对于某个类的所有单元测试均放在同一个文件内

Jacoco代码覆盖率

工程必须配置Jacoco插件,以进行代码测试覆盖率的检查,只有覆盖率达到一定比例才能通过构建。更多细节请参考Sonar代码检测.md

准备工作

pom.xml

修改zorrodemo.service模块的pom.xml,添加mockito依赖

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.10.19</version>
    <scope>test</scope>
</dependency>

Service层单元测试DEMO

public class AttachmentServiceUnitTest {
    private static final Logger log = LoggerFactory.getLogger(AttachmentServiceUnitTest.class);
    @InjectMocks
    private AttachmentService attachmentService = new AttachmentService();
    @Mock
    private AttachmentMapper attachmentMapper;

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

    @Test
    public void query() {
        List<Attachment> attachmentList = new ArrayList<>();
        Attachment attachment = new Attachment();
        attachment.setId("1");
        attachment.setName("attachmentName");
        attachmentList.add(attachment);

        given(attachmentMapper.query(Mockito.any(String.class))).willReturn(attachmentList);

        PageInfo<Attachment> result = attachmentService.query("1.docx", 1, 10);
        log.info("result: {}", result);

        assertTrue(result.getTotal() > 0);
    }
}

Service层集成测试DEMO

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RootApplicationContextConfigForTest.class)
@Transactional //事务默认回滚
public class AttachmentServiceIntegrationTest {
    private static final Logger log = LoggerFactory.getLogger(AttachmentServiceIntegrationTest.class);
    @Autowired
    private AttachmentService attachmentService;

    @Test
    public void save() {
        Attachment attachment = new Attachment();
        attachment.setId("1");
        attachment.setName("test1");
        attachment.setPath("/tmp");

        int result = attachmentService.save(attachment);
        log.info("result: {}", result);

        assertEquals(1, result);
    }

    @Test(expected = DataIntegrityViolationException.class)
    public void saveWithException() {
        Attachment attachment = new Attachment();
        attachment.setId("1");

        int result = attachmentService.save(attachment);
        log.info("result: {}", result);

        assertEquals(1, result);
    }

    @Test
    public void query() {
        PageInfo<Attachment> result = attachmentService.query("1.docx", 1, 10);
        log.info("result: {}", result);

        assertTrue(result.getTotal() > 0);
    }

    @Test
    public void queryWithNotExistName() {
        PageInfo<Attachment> result = attachmentService.query("notexist", 1, 10);
        log.info("result: {}", result);

        assertEquals(0, result.getTotal());
    }

    @Test
    public void delete() {
        Attachment attachment = new Attachment();
        attachment.setId("1");
        attachment.setName("test0");
        attachment.setPath("/tmp");
        attachmentService.save(attachment);

        int result = attachmentService.delete("1");
        log.info("result: {}", result);

        assertTrue(result > 0);
    }
}

Controller层集成测试

  • 无法使用@SpringBootTest,不容易实现
  • 公司框架中controller层基本上无业务逻辑,仅是rest接口的定义。本层测试省略后影响较小。

关于回归测试

  • 方式1: 利用IDEA自带的HTTP client实现。
  • 方式2: 使用更通用的postman+newman编写测试脚本并测试
  • 不强制回归测试
  • 更多细节参考接口回归测试.md

其它说明

  • 尽量简化开发流程,降低上手难度,除公司标准框架外,不引入新的复杂度
  • 不做TDD,不做每个层级的单元测试
  • 仅做service层的集成测试
  • 为避免数据不一致造成的测试失效,后期可考虑引入H2 Database(会小幅度增加开发复杂度,根据情况权衡)