Tuesday, 30 May 2017

Spring Boot Test


An integration test, @SpringBootTest, load the whole of Spring application context. According to observations from the output. It initiates a database connection and a real web server listening on the ports (I see a Tomcat server starts on the port 8080). However,  via @SpringBootTest attributes, the web environment can be re-configured. By default, the web environment should be mock. On such a setup, a mocked servlet container is initiated, rather than a real application server. 

Two major annotations construct a typical Spring Boot (I am using 1.5 release right now) integration test. It looks like this:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class MyTest {

    // ...
    
}
  • @RunWith(SpringRunner.class) tells JUnit to run using Spring’s testing support. SpringRunner is the new name for,SpringJUnit4ClassRunner it’s just a bit easier on the eye. 
  • @SpringBootTest is saying “bootstrap with Spring Boot’s support” (e.g. load application.properties and give me all the Spring Boot goodness)
  • The attributewebEnvironment allows specific “web environments” to be configured for the test. You can start tests with a MOCK servlet environment or with a real HTTP server running on either a RANDOM_PORT or a.DEFINED_PORT  
  • If we want to load a specific configuration, we can use the attributeclasses of @SpringBootTest. In this example, we’ve omitted meansclasses that the test will first attempt to load @Configuration from any inner-classes, and if that fails, it will search for your primary @SpringBootApplication class.
There is another way to test without a server on but having the whole of Spring context(I can see all controller and its method having been mapped.), i.e. using @AutoConfigureMockMvc together with @SpringBootTest to inject a MockMvc instance. Spring uses this MockMVC to send HTTP requests into the DispatcherServlet, instead of a Test Rest Template),  and then hand it off to controllers. It explains there is no need to have a real server on. 

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class ApplicationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void shouldReturnDefaultMessage() throws Exception {
        this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(containsString("Hello World")));
    }
}
We may also test without turning sever on, and load partially Spring context(for a single controller). By this way, we may narrow down the test to a web layer alone,  by using @WebMvcTest.

@RunWith(SpringRunner.class)
@WebMvcTest
public class WebLayerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void shouldReturnDefaultMessage() throws Exception {
        this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(containsString("Hello World")));
    }
}
  • When you start testing real systems, you often find it’s helpful to mock out specific beans. Common scenarios for mocking include simulating services that you can’t use when running tests, or testing failure scenarios that are difficult to trigger in a live system.
With Spring Boot 1.4 you can easily create a Mockito mocks that can replace an existing bean, or create a new one:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SampleTestApplicationWebIntegrationTests {

    @Autowired
    private TestRestTemplate restTemplate;

    @MockBean
    private VehicleDetailsService vehicleDetailsService;

    @Before
    public void setup() {
        given(this.vehicleDetailsService.
            getVehicleDetails("123")
        ).willReturn(
            new VehicleDetails("Honda", "Civic"));
    }

    @Test
    public void test() {
        this.restTemplate.getForEntity("/{username}/vehicle", 
            String.class, "sframework");
    }

}
In this example we’re:
  • Creating a Mockito mock for VehicleDetailsService.
  • Injecting it into the ApplicationContext as a bean using @MockBean
  • Injecting it into the field in the test.
  • Stubbing behavior in the setup method.
  • Trigger something that will ultimately call the mock.
Mocks will be automatically reset across tests. They also form part of the cache key used by Spring Test (so there’s no need to add @DirtiesContext)

Spies work in a similar way. Simply annotate a test field with @SpyBean to have a spy wrap any existing bean in the ApplicationContext.

What is Spring Application Context?


The ApplicationContext provides:

  • Bean factory methods for accessing application components.
  • The ability to load file resources in a generic fashion.
  • The ability to publish events to registered listeners.
  • The ability to resolve messages to support internationalization.
  • Inheritance from a parent context.



References: 
Spring Test Document

Building REST services with Spring

For understanding test in Spring boot, the following link is a good one.
Testing improvements in Spring Boot 1.4  






No comments:

Can Jackson Deserialize Java Time ZonedDateTime

Yes, but must include JSR310. Thus ZonedDateTime can be deserialized directly from JSON response to POJO field. <dependency> <g...