It's August 2016 and the Spring Boot team recently released Spring Boot 1.4 ( on July 28th, 2016 ). Some of the biggest improvements and simplifications are around unit and integration testing. We'll take a focused look at two of the newly added features: @MockBean and @SpyBean

New Dependencies and a New Class Runner

  • two new dependencies were added called spring-boot-test and spring-boot-test-autoconfigure. If you're already using the spring-boot-starter-test "starter-dependency", both will automatically be included.
  • Spring Boot 1.4 builds on the also recently released, Spring Framework 4.3. A small but welcome improvement from the Spring Framework: you can replace @RunWith(SpringJunit4ClassRunner.class) with @RunWith(SpringRunner.class). The SpringRunner class is simply a shortened alias.

@MockBean

The newly introduced annotation, (@MockBean), adds generalized support for mocking of any Spring bean. If you've used Mockito Annotations before, @MockBean is simliar to @Mock but with Spring support.

Suppose we have a simple @Component-annoated class called DailyWeather and a simple @Service-annotated class called WeatherService. Additionally, the DailyWeatherclass relies on the WeatherService as a dependency and is injected via the constructor.

To unit test the DailyWeather component, you can mock the WeatherService dependency by simply annotating it with @MockBean and then use Mockito.when(..) like normal.

x
 
1
@RunWith(SpringRunner.class) 
2
public class DailyWeatherTest {
3
4
    @MockBean
5
    private WeatherService weatherService;
6
7
    @Test
8
    public void dailyWeather() {
9
        // Use Mockito like normal
10
        when(weatherService.getWeather()).thenReturn("cloudy");
11
12
        DailyWeather dailyWeather = new DailyWeather(weatherService);
13
        assertThat(dailyWeather.getWeather(), is("The weather for today is cloudy"));
14
    }
15
}

You're probably thinking, that's neat, but that's not really different than using Mockito Annotations. And you're right, it's not. But it gets better though.

Not only will @MockBean provide you with a mock, it will also add that mock as a bean (as the name suggests) within the ApplicationContext, and override any existing beans either by name or by type

You can use it in several different places.

At the Test Class Level

1
 
1
@RunWith(SpringRunner.class) 
2
@MockBean(WeatherService.class) 
3
public class DailyWeatherTest {
4
5
    @Autowired
6
    private WeatherService weatherService;
7
8
    ...
9
} 

At the Configuration Class Level

1
18
 
1
@RunWith(SpringRunner.class) 
2
public class DailyWeatherTest {
3
4
    @Autowired
5
    private WeatherService weatherService;
6
7
    @Test
8
    public void dailyWeather() {
9
        ...
10
    }
11
12
    @Configuration
13
    @Import(SomeOtherConfiguration.class)
14
    @MockBean(WeatherService.class)
15
    static class Config {
16
17
    }
18
}

At the Configuration Field Level

1
19
 
1
@RunWith(SpringRunner.class) 
2
public class DailyWeatherTest {
3
4
    @Autowired
5
    private WeatherService weatherService;
6
7
    @Test
8
    public void dailyWeather() {
9
        ...
10
    }
11
12
    @Configuration
13
    @Import(SomeOtherConfiguration.class)
14
    static class Config {
15
16
        @MockBean
17
        private WeatherService weatherService;
18
    }
19
}

@SpyBean

Another new annotation introduced, @SpyBean can be used in tests to create spys. A spy wraps an existing bean and "spies on it", allowing it to verify method invocations and provide mocking of individual methods without affecting any of the other methods within the class. You can think of a Spy as a "partial mock".

You can use it in all of the same places as @MockBean

At the Test Class Level

1
13
1
@RunWith(SpringRunner.class) 
2
@SpyBean(WeatherService.class) 
3
public class DailyWeatherTest {
4
5
    @Autowired
6
    private WeatherService weatherService;
7
8
    @Test
9
    public void dailyWeather() {
10
        ...
11
        verify(weatherService).getWeather();
12
    }
13
}

At the Test Field Level

x
1
12
 
1
@RunWith(SpringRunner.class) 
2
public class DailyWeatherTest {
3
4
    @SpyBean
5
    private WeatherService weatherService;
6
7
    @Test
8
    public void dailyWeather() {
9
        ...
10
        verify(weatherService).getWeather();
11
    }
12
}  

At the Configuration Class Level

1
18
1
@RunWith(SpringRunner.class) 
2
public class DailyWeatherTest {
3
4
    @Autowired
5
    private WeatherService weatherService;
6
7
    @Test
8
    public void dailyWeather() {
9
        ...
10
        verify(weatherService).getWeather();
11
    }
12
13
    @Configuration
14
    @SpyBean(WeatherService.class)
15
    static class Config {
16
17
    }
18
}

At the Configuration Field Level

1
20
 
1
@RunWith(SpringRunner.class) 
2
public class DailyWeatherTest {
3
4
    @Autowired
5
    private WeatherService weatherService;
6
7
    @Test
8
    public void dailyWeather() {
9
        ...
10
        verify(weatherService).getWeather();
11
    }
12
13
    @Configuration
14
    static class Config {
15
16
        @SpyBean
17
        private WeatherService weatherService;
18
      
19
    }
20
}