It seems that @WebMvcTest
and @MockBean
are not working as expected. Maybe I'm missing something... I have a controller with some dependencies I'm mocking with @MockBean
, but the application fails to start because it cannot find another bean that I think should not be required in this case.
CONTROLLER:
@RestController
public class ExchangeRateStoreController {
private AddExchangeRate addExchangeRate;
private AddExchangeRateRequestAdapter addExchangeRateRequestAdapter;
private GetExchangeRate getExchangeRate;
private GetExchangeRateRequestAdapter getExchangeRateRequestAdapter;
@Autowired
public ExchangeRateStoreController(ExchangeRateRepository exchangeRateRepository, ExchangeRateDateValidator exchangeRateDateValidator, ExchangeRateView exchangeRateView) {
addExchangeRate = new AddExchangeRate(exchangeRateRepository, exchangeRateDateValidator);
addExchangeRateRequestAdapter = new AddExchangeRateRequestAdapter();
getExchangeRate = new GetExchangeRate(exchangeRateView);
getExchangeRateRequestAdapter = new GetExchangeRateRequestAdapter();
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void create(@RequestBody AddExchangeRateRequest addExchangeRateRequest) {
addExchangeRate.execute(addExchangeRateRequestAdapter.toCommand(addExchangeRateRequest));
}
}
TEST:
@RunWith(SpringRunner.class)
@WebMvcTest(ExchangeRateStoreController.class)
public class ExchangeRateStoreControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
ExchangeRateRepository exchangeRateRepository;
@MockBean
ExchangeRateDateValidator exchangeRateDateValidator;
@MockBean
ExchangeRateView exchangeRateView;
@Test
public void givenValidExchangeRateCommand_whenCreate_thenOK() throws Exception {
String validRequestBody = "{\"from\":\"EUR\",\"to\":\"USD\",\"amount\":1.2345,\"date\":\"2018-11-19\"}";
doNothing().when(exchangeRateDateValidator).validate(any());
doNothing().when(exchangeRateRepository).save(any());
mvc.perform(post("/").content(validRequestBody).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated());
}
APPLICATION:
@SpringBootApplication
@EnableJpaRepositories("com...exchangerate.store.infrastructure.persistence")
@EntityScan("com...exchangerate.store.infrastructure.persistence")
@ComponentScan(basePackages = {"com...exchangerate.store.infrastructure", "com...exchangerate.store.application"} )
public class ExchangeRateStoreApplication {
public static void main(String[] args) {
SpringApplication.run(ExchangeRateStoreApplication.class, args);
}
}
And the error I get when run the test:
APPLICATION FAILED TO START
Description:
A component required a bean named 'entityManagerFactory' that could not be found.
Action:
Consider defining a bean named 'entityManagerFactory' in your configuration.
But, as you can see, entityManagerFactory
is not a controller's dependency. So, why is the test trying to load this bean? I'm mocking all the controller dependencies, so I think it shouldn't do this.
1. @MockBean annotation It allow to mock a class or an interface and to record and verify behaviors on it. It can be used as a class level annotation or on fields in either @Configuration classes, or test classes that are @RunWith the SpringRunner.
With @WebMvcTest, Spring Boot provides everything we need to build web controller tests, but for the tests to be meaningful, we need to remember to cover all of the responsibilities. Otherwise, we may be in for ugly surprises at runtime. The example code from this article is available on github.
Spring boot MockMVC example Learn to use Spring MockMVC to perform integration testing of Spring webmvc controllers. MockMVC class is part of Spring MVC test framework which helps in testing the controllers explicitly starting a Servlet container.
It is fast and favors the isolation of the tested component. If the test needs to rely on the Spring Boot container and we want also to add or mock one of the container beans then @MockBean from Spring Boot is preferred way to add mocks. 3. @MockBean in unit testing
The problem's caused by your use of @EnableJpaRepositories
on your application's main class. By placing it on the main class, you're indicating that JPA repositories must always be enabled, irrespective of which particular slice of functionality you're trying to test.
You can fix your problem by doing one of the following:
@EnableJpaRepositores
and @EntityScan
onto a separate JPA-specific configuration class@EnableJpaRepositories
and @EntityScan
and rely on the auto-configured defaults. For this to work, your repositories and entities will have to be in a sub-package of your main class's package.There's some more information about this in Spring Boot's reference documentation where it says the following:
If you use a test annotation to test a more specific slice of your application, you should avoid adding configuration settings that are specific to a particular area on the main method’s application class.
In this particular case, the configuration setting that is specific to a particular area is @EnableJpaRepositories
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With