When running some @WebMvcTest
s locally, I do not have problems (Spring Boot 1.5.8, gradle 4.6):
@RunWith(SpringRunner.class)
@WebMvcTest(VZNFCController.class)
public class VZNFCControllerTest {
private VZNFCTagAction action1 = new VZNFCTagAction();
private VZNFCTagAction action2 = new VZNFCTagAction();
@Before
public void setUp(){
action1.setMessage("message1");
action2.setMessage("message2");
}
@Autowired
private MockMvc mvc;
@MockBean
private VZNFCTagActionRepository actionRepository;
@MockBean
private MappingMongoConverter mongoConverter;
@Test
@WithMockUser
public void testGetTagList() throws Exception {
given(this.actionRepository.findAll())
.willReturn(Arrays.asList(action1, action2));
this.mvc.perform(get("/api/nfc/tags")
.accept(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk());
}
}
However, when I upload to Atlassian Bitbucket and run ./gradlew test --stacktrace
there, I get the following:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.
Now Bibucket pipelines use a Docker Image (java:8). When I switch back locally to @SpringBoot
and @AutoConfigureMockMvc
I get the same error on both environments. Same DB setup on both environments (same docker image for MongoDB), same everything... Could it be some port does not get mapped when using Docker? I think I do create Servlet requests...
EDIT
Emulating a Bitbucket pipeline build in a Docker container (as suggested here), it seems that mocking out the MappingMongoConverter
and moving to @SpringBootTest
together with @AutoConfigureMockMvc
is enough to get it running. So @WebMvcTest
with only partially mocked out context is enough without a container, but it will fail inside a Docker container such as the one present when using Bitbucket. Why?
Turns out there were some key beans missing, since the @WebMvc
annotation will not pick up @Components
, only what's needed for the web stack not including Repositories, but the issue was my security configuration which I want tested as well and which I now simply @Import
(along with some other beans that the controller depends on):
@RunWith(SpringRunner.class)
@WebMvcTest(value = VZNFCController.class)
@Import(value = {VZApiSecurityConfiguration.class,
VZJwtTokenUtils.class, VZProperties.class})
public class VZNFCControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private VZNFCTagService tagService;
/* same as the above... I don't mock out the repository any more */
}
Lesson learned here is that all the autoconfigured test contexts (@WebMvc
, @DataMongoTest
, etc. ) speed up your tests (which is good, because I am paying build minutes on bitbucket). But you need to really know what's needed to get that slice of your application running. It forced me to really only mock the service to concentrate on the controller and then write some more tests for the DAO part of my application. Which is a good thing I guess.