I want to write controller tests that also test my annotations. What I've read so far is that RestAssured one of the ways to go.
It works smoothly when I only have one controller test in place. However, when having 2 or more controller test classes in place, the @MockBeans seem to not be used properly. Depending on the test execution order, all tests from the first test class succeed, and all others fail.
In the following test run, the PotatoControllerTest was executed first, and then the FooControllerTest.
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles({"test", "httptest"})
class FooControllerTest {
@MockBean
protected FooService mockFooService;
@MockBean
protected BarService mockBarService;
@LocalServerPort
protected int port;
@BeforeEach
public void setup() {
RestAssured.port = port;
RestAssured.authentication = basic(TestSecurityConfiguration.ADMIN_USERNAME, TestSecurityConfiguration.ADMIN_PASSWORD);
RestAssured.requestSpecification = new RequestSpecBuilder()
.setContentType(ContentType.JSON)
.setAccept(ContentType.JSON)
.build();
}
@SneakyThrows
@Test
void deleteFooNotExists() {
final Foo foo = TestUtils.generateTestFoo();
Mockito.doThrow(new DoesNotExistException("missing")).when(mockFooService).delete(foo.getId(), foo.getVersion());
RestAssured.given()
.when().delete("/v1/foo/{id}/{version}", foo.getId(), foo.getVersion())
.then()
.statusCode(HttpStatus.NOT_FOUND.value());
Mockito.verify(mockFooService, times(1)).delete(foo.getId(), foo.getVersion());
}
...
}
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles({"test", "httptest"})
class PotatoControllerTest {
@MockBean
protected PotatoService mockPotatoService;
@LocalServerPort
protected int port;
@BeforeEach
public void setup() {
RestAssured.port = port;
RestAssured.authentication = basic(TestSecurityConfiguration.ADMIN_USERNAME, TestSecurityConfiguration.ADMIN_PASSWORD);
RestAssured.requestSpecification = new RequestSpecBuilder()
.setContentType(ContentType.JSON)
.setAccept(ContentType.JSON)
.build();
}
...
}
Wanted but not invoked:
fooService bean.delete(
"10e76ae4-ec1b-49ce-b162-8a5c587de2a8",
"06db13f1-c4cd-435d-9693-b94c26503d40"
);
-> at com.xxx.service.FooService.delete(FooService.java:197)
Actually, there were zero interactions with this mock.
I tried to fix it with a common ControllerTestBase which configures all mocks and all other controller tests extending the base class. Which worked fine on my machine, but e.g. not in the pipeline. So I guess it is not really stable.
Why is Spring not reloading the context with the mocks? Is this the "best" way of testing my controllers?