系统设计六-单元测试和Mock的应用

单元测试知识

 

百度百科

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

POM依赖

SpringBoot 中的 pom.xml 文件需要添加的依赖

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
        </dependency>        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

@runWith注解作用

注解描述
@RunWith就是一个运行器
@RunWith(JUnit4.class)就是指用JUnit4来运行
@RunWith(SpringJUnit4ClassRunner.class)让测试运行于Spring测试环 境,以便在测试开始的时候自动创建Spring的应用上下文
@RunWith(SpringRunner.class)SpringRunner extends SpringJUnit4ClassRunner
@RunWith(Suite.class)就是一套测试集合
@RunWith(MockitoJUnitRunner.class)使Mockito的注解生效

单元测试注解

注解描述
@Test使用该注解标注的public void方法会表示为一个测试方法
@BeforeClass表示在类中的任意public static void方法执行之前执行
@AfterClass表示在类中的任意public static void方法之后执行
@Before表示在任意使用@Test注解标注的public void方法执行之前执行
@After表示在任意使用@Test注解标注的public void方法执行之后执行

@SpringBootTest 注解作用

属性描述
Class<?>[] classes()指定启动类
SpringBootTest.WebEnvironment webEnvironment()
经常和测试类中@LocalServerPort一起在注入属性时使用。会随机生成一个端口号

@RunWith(JUnit4.class)使用

 定义计算类Calculate

import lombok.extern.slf4j.Slf4j;

/**
 * 计算类定义
 *
 * @author yangyanping
 * @date 2022-04-07
 */
@Slf4j
public class Calculate {

    /**
     * 加法
     */
    public int add(int a, int b) {
        log.info("{} add {}", a, b);
        return a + b;
    }

    /**
     * 减法
     */
    public int sub(int a, int b) {
        log.info("{} sub {}", a, b);
        return a - b;
    }

    /**
     * 乘法
     */
    public int mult(int a, int b) {
        log.info("{} mult {}", a, b);
        
        return a * b;
    }

    /**
     * 除法
     */
    public int dev(int a, int b) {
        log.info("{} dev {}", a, b);
        
        return a / b;
    }
}
@Slf4j
public class InjectCalculate {
    @Autowired
    private Calculate mockCalculate;

    public int add(int a, int b) {
        log.info("{} add {}", a, b);
        return mockCalculate.add(a, b);
    }
}

定义单元测试类CalculateTest

/**
 * 计算类单元测试类定义
 *
 * @author yangyanping
 * @date 2022-04-07
 */
@RunWith(JUnit4.class)
public class CalculateTest {

    private Calculate calculate;

    private int a;
    private int b;

    /**
     * 初始化方法
     */
    @Before
    public void init() {
        calculate = new Calculate();
        a = 10;
        b = 2;
    }

    /**
     * 加法
     */
    @Test
    public void testAdd() {
        Assert.assertEquals(12L, calculate.add(a, b));
    }

    /**
     * 减法
     */
    @Test
    public void testSub() {
        Assert.assertEquals(8L, calculate.sub(a, b));
    }

    /**
     * 乘法
     */
    @Test
    public void testMult() {
        Assert.assertEquals(20L, calculate.mult(a, b));
    }

    /**
     * 除法
     */
    @Test
    public void testDev() {
        Assert.assertEquals(5L, calculate.dev(a, b));
    }

    /**
     * 除法
     */
    @Test(expected = ArithmeticException.class)
    public void testDevException() {
        calculate.dev(a, 0);
    }
}

运行效果

Mock单元测试

Mock的概念

所谓的mock就是创建一个类的虚假的对象,在测试环境中,用来替换掉真实的对象,以达到两大目的:

  1. 验证这个对象的某些方法的调用情况,调用了多少次,参数是什么等等
  2. 指定这个对象的某些方法的行为,返回特定的值,或者是执行特定的动作

Mockito的注解生效

方式一:给被测类添加@RunWith(MockitoJUnitRunner.class)注解

/**
 * 计算类单元测试类定义
 *
 * @author yangyanping
 * @date 2022-04-07
 */
@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class MockCalculateTest {
 }

方式二:在初始化方法中使用MockitoAnnotations.openMocks(MockCalculateTest.class)

    /**
     * 初始化方法
     */
    @BeforeClass
    public static void init() {
        MockitoAnnotations.openMocks(MockCalculateTest.class);
    }

@Mock 和 @Spy

注解描述
@Mock对函数的调用均执行mock(即虚假函数),不执行真正部分
@Spy对函数的调用均执行真正部分。Mockito中的Mock和Spy都可用于拦截那些尚未实现或不期望被真实调用的对象和方法,并为其设置自定义行为。二者的区别在于Mock不真实调用,Spy会真实调用。
@InjectMocks通过创建一个实例,它可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中

MockCalculateTest类定义

/**
 * 计算类单元测试类定义
 *
 * @author yangyanping
 * @date 2022-04-07
 */
@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class MockCalculateTest {

    @Mock
    private static Calculate mockCalculate;

    @Spy
    private static Calculate spyCalculate;

    @InjectMocks
    private InjectCalculate injectCalculate;

    private int a = 10;
    private int b = 2;

    /**
     * 初始化方法
     */
    @BeforeClass
    public static void init() {
        MockitoAnnotations.openMocks(MockCalculateTest.class);
    }

    /**
     * 加法
     */
    @Test
    public void testAdd() {
        // 默认返回结果是返回类型int的默认值
        Assert.assertEquals(0, mockCalculate.add(a, b));
    }

    /**
     * 减法
     */
    @Test
    public void testSub() {
        // 默认会走真实方法
        Assert.assertEquals(8L, spyCalculate.sub(a, b));

        //打桩后,不会走了
        when(spyCalculate.sub(2, 1)).thenReturn(8);
        Assert.assertEquals(8L, spyCalculate.sub(2, 1));

        // 但是参数不匹配的调用,依然走真实方法
        Assert.assertEquals(1L, spyCalculate.sub(4, 3));
    }

    /**
     * 加法
     */
    @Test
    public void testInjectAdd() {
        when(mockCalculate.add(anyInt(), anyInt())).thenReturn(10);
        // 默认返回结果是返回类型int的默认值
        Assert.assertEquals(10, injectCalculate.add(2, 3));
    }
}

运行testAdd

结果没有日志:10 add 2 输出,即没有执行Calculate 类真实的add方法,

运行testSub

输出结果有运行日志,即执行了Calculate 类真实的sub方法

@RunWith(SpringRunner.class)

定义抽象基类

/**
 * 单元测试基础类
 *
 * @author yangyanping
 * @date 2022-04-07
 */
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = WebConfig.class)
public abstract class AbstractBootTest {

    @Before
    public void init() {
        log.info("Spring boot test is being started, please wait ....");
    }
}
WebConfig定义
/**
 * 系统配置类
 * @author yangyanping
 * @date 2021-08-25
 */
@EnableAsync
@EnableCaching
@MapperScan("com.flow.dao")
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ImportResource("classpath:applicationContext-web.xml")
@SpringBootApplication(scanBasePackages = {"com.flow"}, exclude = {DataSourceAutoConfiguration.class})
public class WebConfig {
}
@Slf4j
public class FlowQueryProviderTest extends AbstractBootTest {

    @Autowired
    private FlowQueryProvider flowQueryProvider;

    @Test
    public void testQueryApproveNode(){
        ApproveNodeQuery query = new ApproveNodeQuery();
        query.setApproveErp("xiyangyang");
        query.setApproveStatus(FlowReqNodeStatus.ADOPT.getCode());

        FlowResult<PageResult<FlowReqNodeVo>> flowResult =         
        flowQueryProvider.queryApproveNode(query);

        Assert.assertNotNull(flowResult.getModel());
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值