springboot details of the excavation (support for testing)

Spring Boot provides specialized support component testing Spring Boot Test, which integrates the industry's popular seven kinds of powerful testing framework:

  • JUnit, a Java language framework unit testing;
  • Spring Test, Spring Boot to provide tools to support integration testing and application;
  • AssertJ, supports streaming assertions Java testing framework;
  • Hamcrest, a matching library;
  • Mockito, a Java Mock framework;
  • JSONassert, an assertion library for the JSON;
  • JsonPath,JSON XPath 库。

The seven complete test framework to support software development in a variety of scenarios, we only need to integrate Spring Boot Test in the project to have these 7 various functional testing framework, and use scenarios Spring for Spring Boot project package and optimized for use in Spring Boot to facilitate the project, then introduce the use of Spring Boot Test.

Quick start

We create a spring-boot-test program to demonstrate the use of Spring Boot Test, just add spring-boot-starter-test can rely on in the project:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

Test Methods

First, to demonstrate one of the most simple test, only a test execution methods:

public class HelloTest {
    @Test
    public void hello() {
        System.out.println("hello world");
    }
}

Click helle () method name, select Run hello () to run, finished in Idea console print the following information:

hello world

The method proved successful.

Testing Services

In most cases projects are needed to test the accuracy of a particular service, this time is often needed after the Spring context Boot start, in this case need only add two notes can be supported. We created a HelloService service to demonstrate.

public interface HelloService {
    public void sayHello();
}

Creating a class of its realization:

@Service
public class HelloServieImpl implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("hello service");
    }
}

In this implementation class sayHello () method output string: "hello service".

To be able to acquire context (Beans) after starting the test, Spring Boot Test provides two annotation support, just add @RunWith in the above test class test (SpringRunner.class) and annotations can @SpringBootTest .

@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloServiceTest {
    @Resource
    HelloService helloService;

    @Test
    public void  sayHelloTest(){
        helloService.sayHello();
    }
}

While injecting the test class HelloService, sayHelloTest test method calls HelloService the sayHello () method, after performing the test method, you will find print out the information Spring Boot start in the console, indicating that prior to performing the test method, for Spring Boot container is initialized, after you start the information output will print out the following information:

hello service

Testing services prove successful, but this test will be somewhat cumbersome, because the console print too many things, we need to carefully distinguish between, there is a more elegant solution, can be used to determine whether System output OutputCapture what we wanted content, add OutputCapture transformation as follows.

import static org.assertj.core.api.Assertions.assertThat;
import org.springframework.boot.test.rule.OutputCapture;

@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloServiceTest {
    @Rule
    public OutputCapture outputCapture = new OutputCapture();
    @Resource
    HelloService helloService;
    @Test
    public void  sayHelloTest(){
        helloService.sayHello();
        assertThat(this.outputCapture.toString().contains("hello service")).isTrue();
    }
}

OutputCapture is a test class Spring Boot provided, it can capture System.out and System.err output, we can use this feature to determine whether the output of the program execution.

When the output of such content if "hello service", the test cases executed successfully; if not, it will fail to perform, no longer need to focus on console output.

Web Testing

According to statistics now developing a Java project more than 90% of all Web project, how to test the accuracy of the external interface provides a Web project becomes important. In past experience, we often visit in the browser address some specific test, but if it comes to a number of non get requests will become a little trouble, and some readers will use PostMan tool or write your own HTTP Post requests for testing, but in the end is not easy elegance.

Spring Boot Test has tested solutions for the Web: MockMvc, which achieves simulation of HTTP requests, can be used directly in the form of a network, switch to invoke the Controller, so you can make testing faster and does not depend on the network environment, and providing a verification tool, which can verify that the request is uniform and more convenient.

The next presentation, first add the Web relies in the project:

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Create an external output HelloController a hello method.

@RestController
public class HelloController {
    @RequestMapping(name="/hello")
    public String getHello() {
        return "hello web";
    }
}

HelloWebTest to create a web interface getHello we created above () method for testing.

import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloWebTest {
    private MockMvc mockMvc;
    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.standaloneSetup(new HelloController()).build();
    }
    @Test
    public void testHello() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.post("/hello")
                .accept(MediaType.APPLICATION_JSON_UTF8)).andDo(print());
    }
}
  • Note @Before means that operation before executing test cases need to do here is to initialize the need to establish a test environment.
  • MockMvcRequestBuilders.post refers to the support post request, where you can actually support various types of requests, such as get request, put the request, patch request, delete request and so on.
  • andDo (print ()), andDo (): Add ResultHandler results of the processor, print () print requests and the corresponding content.

Console output:

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /hello
       Parameters = {}
          Headers = {Accept=[application/json;charset=UTF-8]}
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.neo.web.HelloController
           Method = public java.lang.String com.neo.web.HelloController.getUser()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {Content-Type=[application/json;charset=UTF-8], Content-Length=[9]}
     Content type = application/json;charset=UTF-8
             Body = hello web
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

The above output information will find all of the entire process of printing out a request, including a request header, the request parameters, return information, etc., according to the printed information can be known Body HelloController of the getHello () method to test successfully.

But sometimes we do not want to know the whole request process, only need to verify the returned results are correct you can, you can do the following transformation:

@Test
public void testHello() throws Exception {
    mockMvc.perform(MockMvcRequestBuilders.post("/hello")
            .accept(MediaType.APPLICATION_JSON_UTF8))
//                .andDo(print())
             .andExpect(content().string(equalTo("hello web")));
}

If the interface return value is "hello web" test is successful, otherwise the test fails. Also supports authentication result set contains a specific string, then you can use containsString () method to determine.

.andExpect(content().string(containsString("hello")));;

The result set support direct output to a string:

String mvcResult= mockMvc.perform(MockMvcRequestBuilders.get("/messages")).andReturn().getResponse().getContentAsString();
System.out.println("Result === "+mvcResult);

Support passing parameters at the time of the request:

@Test
public void testHelloMore() throws Exception {
    final MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.add("id", "6");
    params.add("hello", "world");
    mockMvc.perform(
             MockMvcRequestBuilders.post("/hello")
            .params(params)
            .contentType(MediaType.APPLICATION_JSON_UTF8)
            .accept(MediaType.APPLICATION_JSON_UTF8))
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("hello")));;
}

If the result is returned JSON syntax may be used to determine the following:

.andExpect(MockMvcResultMatchers.jsonPath("$.name").value("纯洁的微笑"))

MockMvc function provides a set of tools for performing Assert determines which set of tools using a function call chain, spliced ​​together allows multiple test cases, multiple simultaneous determination.

  • Construction perform a request, and returns ResultActions instance, which can be acquired to return requested content.
  • Construction params parameter request time, also support param (key, value) in a continuous addition.
  • contentType (MediaType.APPLICATION_JSON_UTF8) representative of the transmission data format of the transmission side.
  • accept (MediaType.APPLICATION_JSON_UTF8) on behalf of clients willing to accept the type of data format.
  • mockMvc.perform () to establish a Web request.
  • andExpect (...) can Perform (...) function call after multiple calls, showing a plurality of determination conditions.
  • status (). isOk () determines whether the status of the request 200 to return.
  • The method returns MvcResult andReturn object can obtain the name of the view returned Response returned state, acquiring a set of interceptor intercepts the request and the like.

Using JUnit

JUnit is a unit testing framework for the Java language, which is considered the most important third-party Java libraries developed to date. JUnit advantage is that the entire testing process without the participation of people, without having to analyze and judge the final test results are correct, and can easily run more than a one-time test. The latest version of JUnit for Junit 5, Spring Boot default integration is Junit 4.

The following is a commonly used Junit comment:

  • @Test, the test method as a method for marking
  • @Before, is automatically called once before each test method execution
  • @After, each test method is automatically called once finished execution
  • @BeforeClass, once executed all the test methods, the test class has not been loaded already instantiated prior to execution, so static modification with
  • @AfterClass, once executed all the test methods, the test class has not been loaded already instantiated prior to execution, so static modification with
  • @Ignore, does not carry the test method
  • @RunWith When a class with @RunWith comments or inherit a class annotated with @RunWith time, JUnit will call the class to which it refers to the class of the test run rather than go JUnit internal developers to build it. We use this feature to see in the development process.

Create a test class JUnit4Test categories:

public class JUnit4Test {
    Calculation calculation = new Calculation();
    int result;     //测试结果

    //在 JUnit 4 中使用 @Test 标注为测试方法
    @Test
    //测试方法必须是 public void 的
    public void testAdd() {
        System.out.println("---testAdd开始测试---");

        //每个里面只测一次,因为 assertEquals 一旦测试发现错误就抛出异常,不再运行后续代码
        result = calculation.add(1, 2);
        assertEquals(3, result);

        System.out.println("---testAdd正常运行结束---");
    }

    //又一个测试方法
    //timeout 表示测试允许的执行时间毫秒数,expected 表示忽略哪些抛出的异常(不会因为该异常导致测试不通过)
    @Test(timeout = 1, expected = NullPointerException.class)
    public void testSub() {
        System.out.println("---testSub开始测试---");

        result = calculation.sub(3, 2);
        assertEquals(1, result);

        throw new NullPointerException();

        //System.out.println("---testSub正常运行结束---");
    }


    //指示该[静态方法]将在该类的[所有]测试方法执行之[前]执行
    @BeforeClass
    public static void beforeAll() {
        System.out.println("||==BeforeClass==||");
        System.out.println("||==通常在这个方法中加载资源==||");
    }

    //指示该[静态方法]将在该类的[所有]测试方法执行之[后]执行
    @AfterClass
    public static void afterAll() {
        System.out.println("||==AfterClass==||");
        System.out.println("||==通常在这个方法中释放资源==||");
    }

    //该[成员方法]在[每个]测试方法执行之[前]执行
    @Before
    public void beforeEvery() {
        System.out.println("|==Before==|");
    }

    //该[成员方法]在[每个]测试方法执行之[后]执行
    @After
    public void afterEvery() {
        System.out.println("|==After==|");
    }

}

calculator calculation is custom tools, project specific reference sample, after performing the test class, output:

||==BeforeClass==||
||==通常在这个方法中加载资源==||
|==Before==|
---testAdd开始测试---
---testAdd正常运行结束---
|==After==|
|==Before==|
---testSub开始测试---
|==After==|
||==AfterClass==||
||==通常在这个方法中释放资源==||

Contrast the above description can clearly understand the use of each annotation.

Assert use

Assert translated into Chinese as "assertion", used JUnit readers are familiar with this concept, it is established that one of the actual and expected operating values ​​as want, otherwise it throws an exception. Spring into the reference method for detecting borrowed the concept, it provides Assert class has many methods according to the rules of method into the reference assertion, most of the parameter detection method can meet the requirements.

Spring Boot also provides a type of assertion verification, help us verify the method returns the result in the test.

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestAssert {
    @Autowired
    private UserService userService;
    @Test
    public void TestAssert(){
        //验证结果是否为空
        Assert.assertNotNull(userService.getUser());
        //验证结果是否相等
        Assert.assertEquals("i am neo!", userService.getUser());
        //验证条件是否成立
        Assert.assertFalse(1+1>3);
        //验证对象是否相等
        Assert.assertSame(userService,userService);
        int status=404;
        //验证结果集,提示
        Assert.assertFalse("错误,正确的返回值为200", status != 200);
        String[] expectedOutput = {"apple", "mango", "grape"};
        String[] methodOutput = {"apple", "mango", "grape1"};
        //验证数组是否相同
        Assert.assertArrayEquals(expectedOutput, methodOutput);
    }
}

By using the above example can be found, use the Assert can easily verify the test results returned, avoid writing a lot of if / else to judge, to make the code more elegant.

How to use assertThat

JUnit 4 learning JMock, introduced Hamcrest matching mechanism that enables programmers to write unit tests assert statement can be more readable, but also more flexible. Hamcrest is a test framework that provides a common set of matching symbols Matcher, flexible use these rules to match the character defined, programmers can more accurately express their thoughts tests, test conditions specified want to set.

Junit assertion syntax is one of the longest used in the content of the article to start using the text assertThat System output was judged, assertThat fact, JUnit 4 is the latest syntactic sugar, use only assertThat an assertion statement, provided in conjunction with Hamcrest sign, before it can replace the use of all assertions.

AssertThat basic syntax is as follows:

assertThat( [value], [matcher statement] );
  • The next value is the value of the variable you want to test;
  • matcher statement is to use Hamcrest matching symbols to express statement on the front of the variable expectations, expectations expressed in value if the value of the matcher statement match, the test is successful, otherwise the test fails.

General matcher

// allOf 匹配符表明如果接下来的所有条件必须都成立测试才通过,相当于“与”(&&)
assertThat( testedNumber, allOf( greaterThan(8), lessThan(16) ) );
// anyOf 匹配符表明如果接下来的所有条件只要有一个成立则测试通过,相当于“或”(||)
assertThat( testedNumber, anyOf( greaterThan(16), lessThan(8) ) );
// anything 匹配符表明无论什么条件,永远为 true
assertThat( testedNumber, anything() );
// is 匹配符表明如果前面待测的 object 等于后面给出的 object,则测试通过
assertThat( testedString, is( "developerWorks" ) );
// not 匹配符和 is 匹配符正好相反,表明如果前面待测的 object 不等于后面给出的 object,则测试通过
assertThat( testedString, not( "developerWorks" ) );

Related string matcher

// containsString 匹配符表明如果测试的字符串 testedString 包含子字符串"developerWorks"则测试通过
assertThat( testedString, containsString( "developerWorks" ) );
// endsWith 匹配符表明如果测试的字符串 testedString 以子字符串"developerWorks"结尾则测试通过
assertThat( testedString, endsWith( "developerWorks" ) ); 
// startsWith 匹配符表明如果测试的字符串 testedString 以子字符串"developerWorks"开始则测试通过
assertThat( testedString, startsWith( "developerWorks" ) ); 
// equalTo 匹配符表明如果测试的 testedValue 等于 expectedValue 则测试通过,equalTo 可以测试数值之间的字
//符串之间和对象之间是否相等,相当于 Object 的 equals 方法
assertThat( testedValue, equalTo( expectedValue ) ); 
// equalToIgnoringCase 匹配符表明如果测试的字符串 testedString 在忽略大小写的情况下等于
//"developerWorks"则测试通过
assertThat( testedString, equalToIgnoringCase( "developerWorks" ) ); 
// equalToIgnoringWhiteSpace 匹配符表明如果测试的字符串 testedString 在忽略头尾的任意个空格的情况下等
//于"developerWorks"则测试通过,注意,字符串中的空格不能被忽略
assertThat( testedString, equalToIgnoringWhiteSpace( "developerWorks" ) );

Correlation value matcher

// closeTo 匹配符表明如果所测试的浮点型数 testedDouble 在 20.0±0.5 范围之内则测试通过
assertThat( testedDouble, closeTo( 20.0, 0.5 ) );
// greaterThan 匹配符表明如果所测试的数值 testedNumber 大于 16.0 则测试通过
assertThat( testedNumber, greaterThan(16.0) );
// lessThan 匹配符表明如果所测试的数值 testedNumber 小于 16.0 则测试通过
assertThat( testedNumber, lessThan (16.0) );
// greaterThanOrEqualTo 匹配符表明如果所测试的数值 testedNumber 大于等于 16.0 则测试通过
assertThat( testedNumber, greaterThanOrEqualTo (16.0) );
// lessThanOrEqualTo 匹配符表明如果所测试的数值 testedNumber 小于等于 16.0 则测试通过
assertThat( testedNumber, lessThanOrEqualTo (16.0) );

Related matcher collection

// hasEntry 匹配符表明如果测试的 Map 对象 mapObject 含有一个键值为"key"对应元素值为"value"的 Entry 项则
//测试通过
assertThat( mapObject, hasEntry( "key", "value" ) );
// hasItem 匹配符表明如果测试的迭代对象 iterableObject 含有元素“element”项则测试通过
assertThat( iterableObject, hasItem ( "element" ) );
// hasKey 匹配符表明如果测试的 Map 对象 mapObject 含有键值“key”则测试通过
assertThat( mapObject, hasKey ( "key" ) );
// hasValue 匹配符表明如果测试的 Map 对象 mapObject 含有元素值“value”则测试通过
assertThat( mapObject, hasValue ( "key" ) );

Specific use of the reference sample project CalculationTest use.

Junt use a few suggestions:

  • @Test must be modified on the test method
  • Test methods must be modified to use public void can not take any parameters
  • Create a directory to hold the source code for our test code, test code and project business is about to separate from the code
  • Where the test class package name and the package name should be consistent with where the test class
  • Each test unit independent method must be tested, there can be no dependencies between Test Method
  • Test test classes use as a suffix class name (not required)
  • Test Method Test method name as a prefix (not required)

to sum up

Spring Boot is a self-contained testing components of open source software, Spring Boot Test is built into the seven kinds of powerful test tools, covering all aspects of the test, in practical applications only need to import the Spring Boot Test so that the project can have a variety of test function. In the micro-services architecture rigorous three-tier test coverage, in order to effectively ensure project quality.

Guess you like

Origin blog.csdn.net/chehec2010/article/details/94639699