背景

根据 springboot 微服务架构给单元测验带来的问题:

  1. springboot 单元测验启动家长进程十分缓慢,后期服务启动达到分钟级,十分影响效率
  2. 服务之间相互依靠十分严重,单元测验的运行十分依靠其它服务稳定性
  3. 第三方服务和中间件,测验进程发生大量废物数据,污染环境,十分粗笨,乃至发生资损。

解决办法

采用 EasyMock, PowerMock,Mockito 等mock 结构, 屏蔽外部依靠,还原单元测验自身。

Mockito运用

因为spring-boot-starter-test 默认集成了 Mockito的依靠,本文优先介绍 Mockito结构的运用。

依靠

一般不需要手动指定

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>3.3.3</version>
        </dependency>

导入

导入常用静态办法

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

模仿目标

Mock 目标的创建

语法: mock(class or interface)

用例:

OrderService orderService = mock(OrderService.class);

设置预期回来值

语法: when(mock.someMethod()).thenReturn(value)

用例:

when(productService.getById(1L)).thenReturn(new Product(1L,"mate50", new BigDecimal("9000")));

验证被测验类办法

语法: verify(mock).someMethod(…),verify(mock,times(integer)).someMethod(…)

用例:

verify(orderMapper).insert(any(Order.class));
verify(stockService, times(2)).deduct(anyLong(), eq(1));

合作注解运用

语法: @Mock, mock 一个目标

语法: @InjectMocks , 依靠注入 mock目标

用例:

    /**
     * orderMapper, stockService等为 OrderServiceImpl的依靠目标.
     */
    @InjectMocks
    private OrderService orderService = new OrderServiceImpl(); 
    @Mock
    private OrderMapper orderMapper;
    @Mock
    private StockService stockService;
    @Mock
    private AmountService amountService;
    @Mock
    private ProductService productService;

完好案列

    @Test
    public void testSubmitOrder() {
        Long userId = 1L;
        List<Product> productList = Lists.newArrayList(
                new Product(1L, 1),
                new Product(2L, 1)
        );
        // 模仿productService.getById办法,回来一个Product目标
        when(productService.getById(1L)).thenReturn(new Product(1L,"mate50", new BigDecimal("9000")));
        when(productService.getById(2L)).thenReturn(new Product(2L,"充电宝", new BigDecimal("120")));
        boolean result = orderService.submitOrder(userId, productList);
        // submitOrder 验证回来结果为true
        assertTrue(result);
        // 验证stockService.deduct办法被调用了2次
        verify(stockService, times(2)).deduct(anyLong(), eq(1));
        // 验证orderMapper.insert办法被调用了1次
        verify(orderMapper, times(1)).insert(any(Order.class));
    }

运用codeGPT 生成单元测验

单元测试生成最佳实践(GPT+Mockito+JUnit)

单元测试生成最佳实践(GPT+Mockito+JUnit)

完好代码

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class OrderServiceTest {
    @Mock
    private ProductService productService;
    @Mock
    private StockService stockService;
    @Mock
    private OrderMapper orderMapper;
    @InjectMocks
    private OrderService orderService;
    @Test
    public void testSubmitOrder() {
        // given
        List<Product> products = Arrays.asList(
                new Product(1L, "product1", BigDecimal.valueOf(10), 2),
                new Product(2L, "product2", BigDecimal.valueOf(20), 1)
        );
        when(productService.getById(1L)).thenReturn(new Product(1L, "product1", BigDecimal.valueOf(10), 10));
        when(productService.getById(2L)).thenReturn(new Product(2L, "product2", BigDecimal.valueOf(20), 10));
        // when
        boolean result = orderService.submitOrder(123L, products);
        // then
        assertEquals(true, result);
        verify(productService, times(2)).getById(anyLong());
        verify(stockService, times(3)).deduct(anyLong(), anyInt());
        verify(orderMapper).insert(any(Order.class));
    }
}

定论:通过调查,gpt 生成的单元测验跟手动写的单元测验十分附近,几乎直接能够运用。



一点心得

不要去纠结GPT 哪方面做的不好,要多思考,利用GPT能为咱们做什么。