SpringBoot_Junit

来源:互联网 发布:空号检测软件 编辑:程序博客网 时间:2024/06/17 05:53

前言

今天研究了下SpringBoot-Junit做测试,发现此类博客很多,唯一的缺憾就是有点旧,Springboot都升级到1.5.6.RELEASE(idea自动集成版本),咱们也该与时俱进嘛,故此有了下文。在此感谢BraveWangDev,本文是在此篇文章基础之上进行的版本升级。

pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.ideabook</groupId>    <artifactId>springboot_junit</artifactId>    <version>0.0.1-SNAPSHOT</version>    <packaging>jar</packaging>    <name>springboot_junit</name>    <description>Demo project for Spring Boot</description>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>1.5.6.RELEASE</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>        <java.version>1.8</java.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-jpa</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <scope>runtime</scope>        </dependency>        <!-- 测试模块,包括JUnit、Hamcrest、Mockito -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-test</artifactId>            <scope>test</scope>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

(一)基础代码以及注解解释

@RunWith(SpringRunner.class) //引入Spring-test框架支持,查看源码发现SpringRunner继承SpringJUnit4ClassRunner@SpringBootTest(classes = MockServletConfig.class)public class JunitApplicationTest {    private MockMvc mvc;    /*    一、 MockServletContext    由于这是一个新建项目,只有一个helloWord路由,所以我们使用MockServletContext来测试    使用MockServletContext来构建一个空的WebApplicationContext,这样我们创建的HelloController    就可以在@Before函数中创建并传递到MockMvcBuilders.standaloneSetup()函数中。    */    @Before    public void setUp() throws Exception{        mvc = MockMvcBuilders.standaloneSetup(new HelloController()).build();    }    /* 二、注意 status() content() equalTo()三个方法       import static org.hamcrest.Matchers.equalTo;       import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;       import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;    */    @Test    public void getHello() throws Exception{        mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON_UTF8))                .andExpect(status().isOk())                .andExpect(content().string(equalTo("hello!!!")));    }    /* 三、test方法注解介绍    //所有测试方法执行前.执行一次,作用:整体初始化    @BeforeClass    //所有测试方法完成后,执行一次,作用:销毁和释放资源    @AfterClass    //每个测试方法前执行,作用:初始化方法    @Before    //每个测试方法后执行,作用:还原现场    @After    // 测试方法超过1000毫秒,记为超时,测试失败    @Test(timeout = 1000)    // 测试方法期望得到的异常类,如果方法执行没有抛出指定的异常,则测试失败    @Test(expected = Exception.class)    // 执行测试时将忽略掉此方法,如果用于修饰类,则忽略整个类    @Ignore(“not ready yet”)    @Test    @RunWith    在JUnit中有很多个Runner,他们负责调用你的测试代码,每一个Runner都有各自的特殊功能,你要根据需要选择不同的Runner来运行你的测试代码。    如果我们只是简单的做普通Java测试,不涉及spring Web项目,你可以省略@RunWith注解,这样系统会自动使用默认Runner来运行你的代码。    */}

在测试这部分代码的时候,遇到一个有趣的问题:运行getHello()方法的时候,idea输出窗口 要么静悄悄啥都没有,要么红彤彤一片,还以为是自己抄写的代码不准确,囧,断言。。。

controller 代码demo

@RestControllerpublic class HelloController {    @RequestMapping(value = "/hello",method = RequestMethod.GET)    public String helloGet(){        return "hello!!!";    }    @RequestMapping(value = "/hello",method = RequestMethod.POST)    public String helloPost(String name){        return  name + " ,hello!!!";    }    @RequestMapping(value = "/hello",method = RequestMethod.PUT)    public String helloPut(@RequestBody User user){        return user + " ,hello!!!";    }    @RequestMapping(value = "/hello",method = RequestMethod.DELETE)    public String helloDelete(@RequestBody User user){        return user + " ,hello!!!";    }}//restful风格API

(二)参数化测试

1. 断言介绍

 /*    Assert断言方法介绍     1、assertEquals   函数原型:assertEquals([String message],expected,actual)    参数说明:        message是个可选的消息,假如提供,将会在发生错误时报告这个消息。       expected是期望值,通常都是用户指定的内容。        actual是被测试的代码返回的实际值。    例:assertEquals("equals","1","1");    */    private String getStr(){        return "asd";    }    @Test    public void testAssertEquals1(){        assertEquals("不相等","a",getStr());        assertEquals("不相等","as",getStr());    }    /*    函数原型:assertEquals([String message],expected,actual,tolerance)    参数说明:        message是个可选的消息,假如提供,将会在发生错误时报告这个消息。      expected是期望值,通常都是用户指定的内容。      actual是被测试的代码返回的实际值。      tolerance是误差参数,参加比较的两个浮点数在这个误差之内则会被认为是相等的。     */    @Test    public void testAssertEquals2(){        assertEquals("你又不乖了。。。",3.33,10/3,0.04);    }    /*    2、assertTrue   函数原型:assertTrue ([String message],Boolean condition)   参数说明:        message是个可选的消息,假如提供,将会在发生错误时报告这个消息。      condition是待验证的布尔型值。      该断言用来验证给定的布尔型值是否为真,假如结果为假,则验证失败。当然,更有验证为假的测试条件:   函数原型:assertFalse([String message],Boolean condition)          该断言用来验证给定的布尔型值是否为假,假如结果为真,则验证失败。       例: assertTrue("true",1==1);            assertFalse("false",2==1);    */    /*    3、assertNull   函数原型:assertNull([String message],Object object)    参数说明:        message是个可选的消息,假如提供,将会在发生错误时报告这个消息。      object是待验证的对象。      该断言用来验证给定的对象是否为null,假如不为null,则验证失败。相应地,还存在能够验证非null的断言:  函数原型:assertNotNull([String message],Object object)         该断言用来验证给定的对象是否为非null,假如为null,则验证失败。        例:assertNull("null",null);           assertNotNull("not null",new String());    */    @Test    public void testAssertNull(){        //assertNull("null",null);//        List list = new ArrayList();//        assertNull("new ArrayList is not null" ,list);        List  list1 = Collections.emptyList();        assertNull("emptyList is not null",list1);    }    /*     4、assertSame  函数原型:assertSame ([String message], expected,actual)    参数说明:        message是个可选的消息,假如提供,将会在发生错误时报告这个消息。      expected是期望值。      actual是被测试的代码返回的实际值。      该断言用来验证expected参数和actual参数所引用的是否是同一个对象,假如不是,则验证失败。  函数原型:assertNotSame ([String message], expected,actual)        该断言用来验证expected参数和actual参数所引用的是否是不同对象,假如所引用的对象相同,则验证失败。        例:assertSame("same",2,4-2);            assertNotSame("not same",2,4-3);     */    @Test    public void testSame(){        //assertSame("same:这俩对象真不一样!!!",2,4-2);        //assertSame("same:这俩对象真不一样!!!",2L,4-2);        //assertSame("same:这俩对象真不一样!!!",new String("aaa"),new String("aaa"));        //assertSame("same:这俩对象一样!!!","aaa","aaa");        //assertSame("same:这俩对象真不一样!!!","aaa",new String("aaa"));        String a,b;        a = "aaaaa";        b = a;        assertSame("same:这俩对象一样!!!",a,b);        assertSame("same:这俩对象一样!!!",a,"aaaaa");    }    /*    5、fail  函数原型:fail([String message])    参数说明:        message是个可选的消息,假如提供,将会在发生错误时报告这个消息。      该断言会使测试立即失败,通常用在测试不能达到的分支上(如异常)。        测试方法是否进行完毕,如果没有则报错并中断,如下:i 的值不同,结果不同。     */    @Test    public void testFail(){        for (int i = 0 ; i < 10 ; i ++){            if (i == 5){                fail("fail....");            }            System.out.println(i);        }    }    /*    6、assertArrayEquals    函数原型:assertArrayEquals([String message], Object[] expecteds,Object[] actuals)    参数说明:        message是个可选的消息,假如提供,将会在发生错误时报告这个消息,以及不一致的具体信息(数组长度、首次出现不一致的地方..)        expecteds 是多个期望值。      actuals是被测试的代码返回的实际值。     */    @Test    public void testAssertArrayEquals(){        //assertArrayEquals("这俩数组不一样??",new Integer[]{1,3,5,7,9},new Integer[]{1,3,5});        /* java.lang.AssertionError: 这俩数组不一样??: array lengths differed, expected.length=5 actual.length=3 */        //assertArrayEquals("这俩数组不一样??",new Integer[]{1,3,5,7,9},new Integer[]{1,3,5,7,9});        //assertArrayEquals("这俩数组不一样??",new Integer[]{1,3,5,7,9},new Integer[]{2,4,6,8,10});        /*这俩数组不一样??: arrays first differed at element [0]; .... */        assertArrayEquals("这俩数组不一样??",new Integer[]{1,3,5,7,9},new Integer[]{1,3,6,8,10});        /* 这俩数组不一样??: arrays first differed at element [2];            Expected :5            Actual   :6         */    }

(三)参数化测试

@RunWith(Parameterized.class)public class ParameterTest {    private String name;    private String password;    //1.初始化测试参数    @Parameters    public static Collection<?> data(){        System.out.println("===data===========");        return Arrays.asList(new Object[][]{                {"Test","123"},                {"ATest","12"},                {"BTest","123"},        });    }    //2.将@Parameters注解的方法中的Object数组中值的顺序对应    public ParameterTest(String name, String password) {        super();        System.out.println("===ParameterTest===================");        this.name = name;        this.password = password;    }    //3.逻辑测试    /*      import static org.junit.Assert.assertTrue;      assertTrue():断言,如果结果为true,啥事没有                       否则,就会弹出“java.lang.AssertionError” + 错误信息(红红的错误很醒目,还以为代码写错了,囧~~~)    */    @Test    public void test() {        System.out.println("==test==========");        assertTrue(name.contains("Test")==true);        assertTrue(password.equals("12"));    }}

3.api测试

主要针对 get 、post方法的测试,而put、delete测试部分个人感觉 Junit支持的不够,限制太多。
测试的API对象, 仍然是HelloController。

@RunWith(SpringRunner.class)@SpringBootTest(webEnvironment=RANDOM_PORT)// 使用0表示端口号随机,也可以指定端口public class RestApiTest {    private String dateReg;    private Pattern pattern;    private RestTemplate template = new TestRestTemplate().getRestTemplate();    @Value("${local.server.port}")// 注入端口号    private int port;    @Test    public void testApi_Get() throws URISyntaxException {        URI uri = new URI("http://tingapi.ting.baidu.com/v1/restserver/ting?" +                "format=json%E6%88%96xml&calback=&from=webapp_music" +                "&method=baidu.ting.billboard.billList&type=1&size=10&offset=0");        String result = template.getForObject(uri, String.class);        System.err.println(result);    }    @Test    public void testApi_Post() throws URISyntaxException {        String url = "http://localhost:"+port+"/hello";        System.out.println("");        System.err.println("url = " + url);        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();        map.add("name", "value1");        String result = template.postForObject(url, map, String.class);        System.err.println(result);    }    //put测试方法,没有返回值,改用exchange    //exchange 以json形式请求,所以要定义Header,不然会报错,说是不支持这种MediaType。    //而对应的Api方法如下,换了方式就很难测试成功,也是醉醉哒!!    //         @RequestMapping(value = "/hello",method = RequestMethod.PUT)    //         public String helloPut(@RequestBody User user){}    @Test    public void testApi_Put() throws Exception {        String reqJsonStr = "{\"age\":\"200\",\"name\":\"zsssss\"}";        HttpHeaders headers = new HttpHeaders();        headers.setContentType(MediaType.APPLICATION_JSON);        HttpEntity<String> entity = new HttpEntity<String>(reqJsonStr,headers);        String url = "http://localhost:"+port+"/hello";        ResponseEntity<String> exchange = template.exchange(url, HttpMethod.PUT, entity, String.class);        String body = exchange.getBody();        System.err.println(body);        System.err.println(exchange);    }    @Test    public void testApi_Delete() throws Exception {        String reqJsonStr = "{\"age\":\"200\",\"name\":\"zsssss\"}";        HttpHeaders headers = new HttpHeaders();        headers.setContentType(MediaType.APPLICATION_JSON);        HttpEntity<String> entity = new HttpEntity<String>(reqJsonStr,headers);        String url = "http://localhost:"+port+"/hello";        ResponseEntity<String> exchange = template.exchange(url, HttpMethod.DELETE, entity, String.class);        String body = exchange.getBody();        System.err.println(body);        System.err.println(exchange);    }    // put delete 虽然说是测试通过了,但是限制忒多,感觉不实用。    // 如 entity的设置,貌似只能用json形式的参数。 其他形式试了很久也没成功,囧。。。。欢迎补充!!!}

(四)打包测试

import org.junit.runner.RunWith;import org.junit.runners.Suite;/** * @Author: lzh * @Description: 打包测试,就是新增一个类,将其他测试类配置在一起,运行这个类达到运行多个测试类的目的 * @Date: Created in 2017/8/10 14:20 */@RunWith(Suite.class)@Suite.SuiteClasses({JunitApplicationTest.class,JunitApplicationTest.class,SpringBootJunitApplicationTests.class})public class SuiteTest {    // 类中不需要编写代码}

代码下载链接