SpringBoot_Junit

前言

今天研究了下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

@RestController
public 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 {
    // 类中不需要编写代码
}

代码下载链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值