First of all I would like to state that I'm fairly new to Spring Boot and Spring Cloud.
I have the following project setup:
Dependencies on the producer side are:
When using the routines spring-cloud-contract:generateTests
everything runs fine and tests are generated in the proper directory, then when running the lifecycle routine test
and those generated contract tests are run they produce the following error:
NoClassDefFoundError: javax/servlet/Filter
at com.projectId.artifactId.contracts.ConsumerTest.validate_shouldReturnApplicationRoles(ConsumerTest.java:113)
projectId
, artifactId
, ConsumerTest
are placeholders for the real package, artifact name and consumer name respectively. Sample of the method where it fails (all generated tests fail and give the same error, line 113 in this case refers to the point of get("/auth/v1/role")
):
public class ConsumerTest extends BaseTestClass {
@Test
public void validate_shouldReturnApplicationRoles() throws Exception {
// given:
MockMvcRequestSpecification request = given();
// when:
ResponseOptions response = given().spec(request)
.get("/auth/v1/role");
// then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).matches("application/json.*");
assertThat(response.header("Access-Control-Allow-Origin")).isEqualTo("*");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).array().contains("['id']").isEqualTo(1);
assertThatJson(parsedJson).array().contains("['name']").isEqualTo("Acotado");
assertThatJson(parsedJson).array().contains("['description']").isEqualTo("Acceso acotado en un espacio temporal desde que se crea la clave");
assertThatJson(parsedJson).array().contains("['id']").isEqualTo(2);
assertThatJson(parsedJson).array().contains("['name']").isEqualTo("Ilimitado");
assertThatJson(parsedJson).array().contains("['description']").isEqualTo("Acceso ilimitado");
}
}
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
@ActiveProfiles("test")
public abstract class BaseTestClass {
@Autowired
AuthorizationController authorizationController;
@Autowired
IdentityController identityController;
@MockBean
AuthorizationService authorizationService;
@MockBean
IdentityService identityService;
@BeforeEach
public void setup() {
RestAssuredMockMvc.standaloneSetup(authorizationController, identityController);
}
}
The test profile just configures a context path and a h2 instance for the integration tests of the application.
I have tried different BaseClasses for tests and expected the tests to pass or at least to fail in a meaningful way. What is failing here? I have a suspicion that Spring Security has something to do with that error...
On an empty project with only Spring Boot and Spring Cloud Contract as dependencies the test do run and sure do fail but as expected in a meaningful way (comparing responses expected against obtained). BaseClass
does differ a little bit since on this trial scenario there is only a mock controller.
I've added Spring Security to the aforementioned mock project and that has been enough to make it fail to compile. I have tried with two BaseTestClass
definitions, one following Spring Cloud Contract's guidelines (which is the one that worked previously) and another following Spring Security's guides on MockMvc
, both below by mention order:
class BaseTestClass {
@BeforeAll
public static void setup() {
RestAssuredMockMvc.standaloneSetup(new TrialController);
}
}
class BaseTestClass{
MockMvc mockMvc;
@BeforeEach
void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new TrialController()).build();
}
}
Neither manages to run the application nor the test producing either IllegalState
errors or spring test delegate cannot be null
.
Okay so after countless hours of going back and forth through the many (and confusing) Spring Docs pages and guides and reading many almost related SO questions and answers here is how I got the application and the tests running.
The BaseTestClass
should look like this:
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.servlet.MockMvc;
import org.springframework.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
@SpringBootTest
@ActiveProfiles("test")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public abstract class BaseTestClass {
private MockMvc mvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
FilterChainProxy springSecurityFilterChain;
@BeforeAll
public void setup() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(springSecurity(springSecurityFilterChain))
.build();
RestAssuredMockMvc.mockMvc(mvc);
}
}
The ActiveProfiles
annotation is just necessary if you need any application configuration for the tests to run, in my case the connection to an h2 instance.
The TestInstance
annotation is to allow the BeforeAll
annotated method not to be static in the same fashion as the docs use JUnit4's Before
.
Hope it helps somebody else.