亲宝软件园·资讯

展开

SpringBoot实现单元测试示例详解

Decade0712 人气:0

一、常用注解

官方文档:Junit5官网指导

package com.decade;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.concurrent.TimeUnit;
@DisplayName("测试类的名称为MyTest")
@SpringBootTest
public class MyTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @DisplayName("测试方法名称为test")
    @RepeatedTest(value = 3)
    public void test() {
        System.out.println("测试方法,打印JdbcTemplate类" + jdbcTemplate);
    }
    @Test
    @Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
    public void testTimeOut() throws InterruptedException {
        Thread.sleep(600);
    }
    @BeforeEach
    public void beforeEach() {
        System.out.println("每个方法前执行");
    }
    @AfterEach
    public void afterEach() {
        System.out.println("每个方法后执行");
    }
    @BeforeAll
    static void beforeAll() {
        System.out.println("所有方法前执行");
    }
    @AfterAll
    static void afterAll() {
        System.out.println("所有方法后执行");
    }
    @Disabled
    @Test
    public void disableTest() {
        System.out.println("此内容不输出");
    }
}

二、断言机制

断言是测试方法中的核心部分,它用于检查业务逻辑返回的数据是否合理,不满足的断言会使得测试方法失败

如果是多个断言依次执行,只要前面的断言不通过,后面的就不会再执行了

1、简单断言

2、数组断言

通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等

3、组合断言

通过assertAll方法接受多个函数式接口的实例作为要验证的断言,只要有一个不通过,就算失败

4、异常断言

通过assertThrow方法断定某个代码会抛出指定异常

5、超时异常

通过assertTimeout断定某个代码的执行时间会超过限制时间

6、快速失败

通过fail方法直接使得测试失败

package com.decade;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
@DisplayName("测试类为TestAssert")
public class TestAssert {
    @Test
    @DisplayName("测试普通断言")
    public void testSimpleAssert() {
        Assertions.assertEquals(6, 2+3, "期望值与实际值不符");
        Assertions.assertTrue(1 > 2, "条件不成立");
        Object a = new Object();
        Object b = new Object();
        Assertions.assertSame(a, b, "不是同一个对象");
    }
    @Test
    @DisplayName("测试数组断言")
    public void testArray() {
        Assertions.assertArrayEquals(new int[]{1, 2}, new int[]{2,1}, "数组不相等");
    }
    @Test
    @DisplayName("测试组合断言")
    public void testCombination() {
        Assertions.assertAll("断言组合1",
            () -> Assertions.assertTrue(1 < 2, "判断条件不成立"),
            () -> Assertions.assertEquals(2, 3, "期望值与实际值不相符"));
    }
    @Test
    @DisplayName("测试异常断言")
    public void testException() {
        Assertions.assertThrows(ArithmeticException.class, () -> {
            System.out.println(1/0);
        }, "并没有抛出算数异常");
    }
    @Test
    @DisplayName("测试超时断言")
    public void testTimeOut() {
        Assertions.assertTimeout(Duration.of(100, ChronoUnit.MILLIS),
            () -> Thread.sleep(500), "超时");
    }
    @Test
    @DisplayName("测试快速失败fail")
    public void shouldFail() {
        Assertions.fail("This should fail");
    }
}

三、前置条件

前置和断言的不同之处在于,不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止

前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要

package com.decade;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("测试类为Test")
public class Test {
    @Test
    @DisplayName("测试前置条件")
    public void testAssumptions() {
        Assumptions.assumeTrue(1 > 2, "条件不成立,后续步骤不再执行");
        System.out.println("前置条件成立,继续执行到此步骤");
    }
}

四、嵌套测试

可以理解成测试套娃,需要注意的是,外层的测试方法无法驱动内层的测试方法,去执行内层测试方法的beforeEach、beforeAll、afterEach、afterAll

package com.decade;
import org.junit.jupiter.api.*;
import java.util.EmptyStackException;
import java.util.Stack;
@DisplayName("进行嵌套测试")
public class TestAStackDemo {
    Stack<Object> stack;
    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
        // 外层的test方法无法驱动内层的test,也就无法驱动下方的BeforeEach去实例化stack,所以这里会报空
        Assertions.assertNull(stack);
    }
    @Nested
    @DisplayName("when new")
    class WhenNew {
        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }
        @Test
        @DisplayName("is empty")
        void isEmpty() {
            Assertions.assertTrue(stack.isEmpty());
        }
        @Test
        @DisplayName("throws EmptyStackException when popped")
        void throwsExceptionWhenPopped() {
            Assertions.assertThrows(EmptyStackException.class, stack::pop);
        }
        @Test
        @DisplayName("throws EmptyStackException when peeked")
        void throwsExceptionWhenPeeked() {
            Assertions. assertThrows(EmptyStackException.class, stack::peek);
        }
        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {
            String anElement = "an element";
            // 内层的test可以驱动外层的test,这里会驱动上方的beforeEach去实例化stack
            @BeforeEach
            void pushAnElement() {
                stack.push(anElement);
            }
            @Test
            @DisplayName("it is no longer empty")
            void isNotEmpty() {
                Assertions.assertFalse(stack.isEmpty());
            }
            @Test
            @DisplayName("returns the element when popped and is empty")
            void returnElementWhenPopped() {
                Assertions.assertEquals(anElement, stack.pop());
                Assertions.assertTrue(stack.isEmpty());
            }
            @Test
            @DisplayName("returns the element when peeked but remains not empty")
            void returnElementWhenPeeked() {
                Assertions.assertEquals(anElement, stack.peek());
                Assertions.assertFalse(stack.isEmpty());
            }
        }
    }
}

五、参数化测试

package com.decade;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.stream.Stream;
@DisplayName("进行参数化测试")
public class Test {
    @ParameterizedTest
    @ValueSource(strings = {"decade", "hhh", "LOL"})
    @DisplayName("参数化测试,传入String类型依次测试")
    public void testParameter(String variables) {
        System.out.println(variables);
    }
    @ParameterizedTest
    // 此处为方法名,方法必须返回stream流,并且是静态的
    @MethodSource("creatStream")
    @DisplayName("参数化测试,传入String类型依次测试")
    public void testParameterIsMethod(String variables) {
        System.out.println(variables);
    }
    static Stream<String> creatStream() {
        return Stream.of("decade", "hhh", "basketball");
    }
}

测试结果如图

加载全部内容

相关教程
猜你喜欢
用户评论