IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> Junit5基础教程 -> 正文阅读

[开发测试]Junit5基础教程

一、概述

单元测试的优点
  1. 提升软件质量
  2. 促进代码优化
单元测试的基本原则
  1. 单元测试要符合AIR原则
  • A : Automatic (自动化)
  • I : Independent (独立性)
  • R : Repeatable (可重复)
  1. 单元测试的代码层面要符合BCDE原则
  • B: Border 边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等
  • C: Correct 正确的输入,并得到预期的结果
  • D: Design 与设计文档相结合,来编写单元测试
  • E : Error 单元测试的目标是证明程序有错,而不是程序无错。为了发现代码中潜在的错误,我们需要在编写测试用例时有一些强制的错误输入(如非法数据、异常流程、非业务允许输入等)来得到预期的错误结果

二、Junit5注解

注解用法说明
@Test表明方法是一个测试方法
@DisplayName为测试类或者测试方法自定义一个名称
@BeforeAll在所有测试方法运行前运行,并且只能修饰静态方法(除非修改测试实例生命周期)
@BeforeEach每个测试方法运行前运行
@AfterEach每个测试方法运行完毕后运行
@AfterAll在所有测试方法运行完毕后运行
@Disabled这个测试不会运行
@RepeatedTest重复测试
@TestMethodOrder指定测试顺序排序方式
@Order注解定义测试顺序
package com.flamingo.junit5test;

import com.flamingo.junit5test.util.Add;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * 常用注解测试
 * @author flamingo
 * @create 7/6/21 11:04 PM
 */
@DisplayName("Common annotation test")
public class AnnotationsTest {
    private static Add add;

    @BeforeAll
    public static void beforeAll() {
        add = new Add();
        System.out.println("Run before all test methods run");
    }

    @BeforeEach
    public void beforeEach() {
        System.out.println("Run before each test method runs");
    }

    @AfterEach
    public void afterEach() {
        System.out.println("Run after each test method finishes running");
    }

    @AfterAll
    public static void afterAll() {
        System.out.println("Run after all test methods have finished running");
    }

    @Disabled
    @Test
    @DisplayName("Ignore the test")
    public void disabledTest() {
        System.out.println("This test will not run");
    }

    @Test
    @DisplayName("Test Methods 1+1")
    public void testAdd1() {
        System.out.println("Running test method1+1");
        Assertions.assertEquals(2, add.add(1, 1));
    }

    @Test
    @DisplayName("Test Methods 2+2")
    public void testAdd2() {
        System.out.println("Running test method2+2");
        Assertions.assertEquals(4,add.add(2,2));
    }
}

三、断言

常用断言断言说明
assertEquals断言预期值和实际值相等
assertAll分组断言,执行其中包含的所有断言
assertArrayEquals断言预期数组和实际数组相等
assertFalse断言条件为假
assertNotNull断言不为空|
assertSame断言两个对象相等
assertTimeout断言超时
assertThrows断言异常,抛出指定的异常,测试才会通过
assertTimeoutPreemptively断言超时,如果在定时内任务没有执行完毕,会立即返回断言失败
package com.flamingo.junit5test;

import org.junit.jupiter.api.Test;

import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.*;

/**
 * @author flamingo
 * @create 7/6/21 11:19 PM
 */
public class Assert {
    @Test
    void standardAssertions() {
        assertEquals(2, 2);
        assertEquals(4, 4, "error message");
        assertTrue(2 == 2, () -> "error message");
    }

    /**
     * 分组断言,执行分组中所有断言,分组中任何一个断言错误都会一起报告
     */
    @Test
    void groupedAssertions() {
        assertAll("person", () -> assertEquals("John", "John"), () -> assertEquals("Doe", "Doe"));
    }

    @Test
    void dependentAssertions() {
        // 分组断言
        assertAll("properties", () -> {
            // 在代码块中,如果断言失败,后面的代码将不会运行
            String firstName = "John";
            assertNotNull(firstName);
            // 只有前一个断言通过才会运行
            assertAll("first name", () -> assertTrue(firstName.startsWith("J")),
                    () -> assertTrue(firstName.endsWith("n")));
        }, () -> {
            // 分组断言,不会受到first Name代码块的影响,所以即使上面的断言执行失败,这里的依旧会执行
            String lastName = "Doe";
            assertNotNull(lastName);
            // 只有前一个断言通过才会运行
            assertAll("last name", () -> assertTrue(lastName.startsWith("D")),
                    () -> assertTrue(lastName.endsWith("e")));
        });
    }

    @Test
    void exceptionTesting() {
        // 断言异常,抛出指定的异常,测试才会通过
        Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
            throw new IllegalArgumentException("a message");
        });
        assertEquals("a message", exception.getMessage());
    }

    @Test
    void timeoutNotExceeded() {
        // 断言超时
        assertTimeout(ofMinutes(2), () -> {
            // 完成任务小于2分钟时,测试通过。
        });
    }

    @Test
    void timeoutNotExceededWithResult() {

        // 断言成功并返回结果
        String actualResult = assertTimeout(ofMinutes(2), () -> {
            return "result";
        });
        assertEquals("result", actualResult);
    }

    @Test
    void timeoutExceeded() {

        // 断言超时,会在任务执行完毕后才返回,也就是1000毫秒后返回结果
        assertTimeout(ofMillis(10), () -> {
            // 执行任务花费时间1000毫秒
            Thread.sleep(1000);
        });
    }

    @Test
    void timeoutExceededWithPreemptiveTermination() {

        // 断言超时,如果在10毫秒内任务没有执行完毕,会立即返回断言失败,不会等到1000毫秒后
        assertTimeoutPreemptively(ofMillis(10), () -> {
            Thread.sleep(1000);
        });
    }

}

3.2 AssertJ

AssertJ 的最大特点是流式断言(Fluent Assertions),与Build Chain 模式或Java stream&filter 写法类似。它允许一个目标对象通过各种Fluent Assert API连接判断进行多次断言并且对IDE更友好。

package com.flamingo.junit5test;

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * @author flamingo
 * @create 7/7/21 8:59 AM
 */
public class AssertJSampleTest {

    @Test
    public void testUsingAssertJ() {
        // 字符串判断
        String s = "abcde";
        assertThat(s).as("字符串判断,判断首尾及长度").startsWith("a").endsWith("e").hasSize(5);
        // 数字判断
        Integer i = 50;
        assertThat(i).as("数字判断,数字大小比较").isGreaterThan(10).isLessThan(100);
        // 日期判断
        Date date1 = new Date();
        Date date2 = new Date(date1.getTime() + 100);
        Date date3 = new Date(date1.getTime() - 100);
        assertThat(date1).as("日期判断:日期大小比较").isBefore(date2).isAfter(date3);
        // list比较
        List<String> list = Arrays.asList("a", "b", "c", "d");
        assertThat(list).as("list的首尾元素及长度").startsWith("a").endsWith("d").hasSize(4);
        // Map判断
        Map<String, Object> map = new HashMap<>();
        map.put("A", 1);
        map.put("B", 2);
        map.put("C", 3);
        assertThat(map).as("Map的长度及键值测试").hasSize(3).containsKeys("x cA","B","C");
    }
}

四、Junit条件和假设

条件假设说明
assumeFalse假设为false时才会执行,如果为true,那么将会直接停止执行
assumeTrue假设为true时才会执行,如果为false,那么将会直接停止执行
assumingThatassumingThat接受一个函数式接口Executable,假设为true时执行,将会执行Executable,否则不会执行Executable
package com.flamingo.junit5test;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;

/**
 * @author flamingo
 * @create 7/7/21 11:23 AM
 */
public class Assumption {
    @Test
    void assumeTrueTest() {
        //如果假设传入的值为True,那么就会执行后面测试,否则直接停止执行
        assumeTrue(false);
        System.out.println("This will not be implemented.");
    }

    @Test
    void assumeFalseTest() {
        //如果假设传入的值为false,那么就会执行后面测试,否则直接停止执行
        assumeFalse(true);
        System.out.println("This will not be implemented.");
    }

    @Test
    void assumingThatTest() {
        //    assumingThat?(boolean assumption, Executable executable)
        //assumingThat 接受一个boolean值assumption,如果assumption为true,那么将会执行executable,否则不会执行,
        //但是assumingThat即使为false也不会影响后续代码的执行,他和assumeFalse和assumeTrue不同,assumingThat只
        //决定Executable是否执行,Executable是一个函数式接口,接受一个没有参数和返回值的方法。
        assumingThat(false, () -> {
            System.out.println("This will not be implemented.");
        });

        //下面的输出将会执行
        System.out.println("This will be implemented.");
    }
}

五、重复测试

通过@RepeatedTest注解可以完成重复测试的工作;通过TestInfo对象可以获得当前测试类或方法的信息;通过RepetitionInfo对象可以获取到重复测试中的信息。

package com.flamingo.junit5test;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
import org.junit.jupiter.api.TestInfo;

/**
 * @author flamingo
 * @create 7/7/21 11:53 AM
 */
public class Repeated {
    @RepeatedTest(value = 10, name = RepeatedTest.LONG_DISPLAY_NAME)
    @DisplayName("repeatTest")
    void repeatedTest(TestInfo testInfo, RepetitionInfo repetitionInfo) {
        //我们可以通过TestInfo在测试中获取测试的相关信息,比如输出自定义的测试名
        System.out.println(testInfo.getDisplayName());
        //输出当前重复次数
        System.out.println("currentRepetition:"+repetitionInfo.getCurrentRepetition());
    }
}

六、自定义测试顺序

@TestMethodOrder注解启用测试顺序,并且可指定三种测试顺序。

设置值说明
MethodOrderer.OrderAnnotation.class使用@Order注解指定的值,值小先执行
MethodOrderer.MethodName.class根据方法名称的字典序来执行
MethodOrderer.Random.class随机顺序执行
package com.flamingo.junit5test;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author flamingo
 * @create 7/7/21 2:43 PM
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class OrderedTest {
    @Order(7)
    @Test
    public void aMethod() {
        System.out.println("A Method");
        assertEquals(1, 1);
    }

    @Order(2)
    @Test
    @Disabled
    public void bMethod() {
        System.out.println("B Method");
        assertEquals(1, 1);
    }

    @Order(3)
    @Test
    public void cMethod() {
        System.out.println("C Method");
        assertEquals(1, 1);
    }

    @Order(4)
    @Test
    public void dMethod() {
        System.out.println("D Method");
        assertEquals(1, 1);
    }
}

七、MockMvc测试控制器

为了可以对Controller进行测试,可以通过引入MockMVC进行解决。
MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。

MockMvc测试过程如下
  1. mockMvc.perform执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理
  2. MockMvcRequestBuilders.post(“http://127.0.0.1:8888/login“)构造一个请求
  3. ResultActions.andExpect添加执行完成后的断言
  4. ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
  5. ResultActions.andReturn表示执行完成后返回相应的结果
MockMvc涉及的API有很多,这里记下来以作查验
RequestBuilder

mockMvc.perform执行一个RequestBuilder请求,RequestBuilder主要有两个子类MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder(如文件上传使用).可以用MockMvcRequestBuilders 的静态方法构建RequestBuilder对象。 MockHttpServletRequestBuilder API:

MockHttpServletRequestBuilder header(String name, Object… values)/MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders):添加头信息;
MockHttpServletRequestBuilder contentType(MediaType mediaType):指定请求的contentType头信息;
MockHttpServletRequestBuilder accept(MediaType… mediaTypes)/MockHttpServletRequestBuilder accept(String… mediaTypes):指定请求的Accept头信息;
MockHttpServletRequestBuilder content(byte[] content)/MockHttpServletRequestBuilder content(String content):指定请求Body体内容;
MockHttpServletRequestBuilder cookie(Cookie… cookies):指定请求的Cookie;
MockHttpServletRequestBuilder locale(Locale locale):指定请求的Locale;
MockHttpServletRequestBuilder characterEncoding(String encoding):指定请求字符编码;
MockHttpServletRequestBuilder requestAttr(String name, Object value) :设置请求属性数据;
MockHttpServletRequestBuilder sessionAttr(String name, Object value)/MockHttpServletRequestBuilder sessionAttrs(Map sessionAttributes):设置请求session属性数据;
MockHttpServletRequestBuilder flashAttr(String name, Object value)/MockHttpServletRequestBuilder flashAttrs(Map flashAttributes):指定请求的flash信息,比如重定向后的属性信息;
MockHttpServletRequestBuilder session(MockHttpSession session) :指定请求的Session;
MockHttpServletRequestBuilder principal(Principal principal) :指定请求的Principal;
MockHttpServletRequestBuilder contextPath(String contextPath) :指定请求的上下文路径,必须以“/”开头,且不能以“/”结尾;
MockHttpServletRequestBuilder pathInfo(String pathInfo) :请求的路径信息,必须以“/”开头;
MockHttpServletRequestBuilder secure(boolean secure):请求是否使用安全通道;
MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor):请求的后处理器,用于自定义一些请求处理的扩展点; MockMultipartHttpServletRequestBuilder继承自MockHttpServletRequestBuilder,又提供了如下API:
MockMultipartHttpServletRequestBuilder file(String name, byte[] content)/MockMultipartHttpServletRequestBuilder file(MockMultipartFile file):指定要上传的文件;

MockMvcRequestBuilders的主要方法

MockHttpServletRequestBuilder get(String urlTemplate, Object… urlVariables):根据uri模板和uri变量值得到一个GET请求方式的MockHttpServletRequestBuilder;如get("/user/{id}", 1L);
MockHttpServletRequestBuilder post(String urlTemplate, Object… urlVariables):同get类似,但是是POST方法;
MockHttpServletRequestBuilder put(String urlTemplate, Object… urlVariables):同get类似,但是是PUT方法;
MockHttpServletRequestBuilder delete(String urlTemplate, Object… urlVariables) :同get类似,但是是DELETE方法;
MockHttpServletRequestBuilder options(String urlTemplate, Object… urlVariables):同get类似,但是是OPTIONS方法;
MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object… urlVariables):提供自己的Http请求方法及uri模板和uri变量,如上API都是委托给这个API;
MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object… urlVariables):提供文件上传方式的请求,得到MockMultipartHttpServletRequestBuilder;
RequestBuilder asyncDispatch(final MvcResult mvcResult):创建一个从启动异步处理的请求的MvcResult进行异步分派的RequestBuilder;

ResultActions

调用MockMvc.perform(RequestBuilder requestBuilder)后将得到ResultActions,通过ResultActions完成如下三件事:

ResultActions andExpect(ResultMatcher matcher) :添加验证断言来判断执行请求后的结果是否是预期的;
ResultActions andDo(ResultHandler handler) :添加结果处理器,用于对验证成功后执行的动作,如输出下请求/结果信息用于调试;
MvcResult andReturn() :返回验证成功后的MvcResult;用于自定义验证/下一步的异步处理;

ResultMatcher

ResultMatcher用来匹配执行完请求后的结果验证,其就一个match(MvcResult result)断言方法,如果匹配失败将抛出相应的异常;spring mvc测试框架提供了很多xxxResultMatchers来满足测试需求。注意这些ResultMatchers并不是ResultMatcher的子类,而是返回ResultMatcher实例的。Spring mvc测试框架为了测试方便提供了MockMvcResultMatchers静态工厂方法方便操作;具体的API如下:

HandlerResultMatchers handler():请求的Handler验证器,比如验证处理器类型/方法名;此处的Handler其实就是处理请求的控制器;
RequestResultMatchers request():得到RequestResultMatchers验证器;
ModelResultMatchers model():得到模型验证器;
ViewResultMatchers view():得到视图验证器;
FlashAttributeResultMatchers flash():得到Flash属性验证;
StatusResultMatchers status():得到响应状态验证器;
HeaderResultMatchers header():得到响应Header验证器;
CookieResultMatchers cookie():得到响应Cookie验证器;
ContentResultMatchers content():得到响应内容验证器;
JsonPathResultMatchers jsonPath(String expression, Object … args)/ResultMatcher jsonPath(String expression, Matcher matcher):得到Json表达式验证器;
XpathResultMatchers xpath(String expression, Object… args)/XpathResultMatchers xpath(String expression, Map namespaces, Object… args):得到Xpath表达式验证器;
ResultMatcher forwardedUrl(final String expectedUrl):验证处理完请求后转发的url(绝对匹配);
ResultMatcher forwardedUrlPattern(final String urlPattern):验证处理完请求后转发的url(Ant风格模式匹配,@since spring4);
ResultMatcher redirectedUrl(final String expectedUrl):验证处理完请求后重定向的url(绝对匹配);
ResultMatcher redirectedUrlPattern(final String expectedUrl):验证处理完请求后重定向的url(Ant风格模式匹配,@since spring4);
得到相应的xxxResultMatchers后,接着再调用其相应的API得到ResultMatcher,如ModelResultMatchers.attributeExists(final String… names)判断Model属性是否存在。具体请查看相应的API。再次就不一一列举了。

MvcResult

即执行完控制器后得到的整个结果,并不仅仅是返回值,其包含了测试时需要的所有信息,如:

MockHttpServletRequest getRequest():得到执行的请求;
MockHttpServletResponse getResponse():得到执行后的响应;
Object getHandler():得到执行的处理器,一般就是控制器;
HandlerInterceptor[] getInterceptors():得到对处理器进行拦截的拦截器;
ModelAndView getModelAndView():得到执行后的ModelAndView;
Exception getResolvedException():得到HandlerExceptionResolver解析后的异常;
FlashMap getFlashMap():得到FlashMap;
Object getAsyncResult()/Object getAsyncResult(long timeout):得到异步执行的结果;

一个简单的例子
package com.flamingo.junit5test;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;

/**
 * @author flamingo
 * @create 7/7/21 3:55 PM
 */
@SpringBootTest
@AutoConfigureMockMvc
public class MockMvcTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void getTest() throws Exception{
        MvcResult mvcResult = mockMvc.perform(
                MockMvcRequestBuilders.get("/hello/sayHi/{name}", "ming")
        ).andDo(MockMvcResultHandlers.print()).andReturn();
        System.out.println(mvcResult.getResponse().getContentAsString());
    }
}

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-07-09 17:40:17  更:2021-07-09 17:40:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 17:58:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码