英文:
Spring Cloud contract tests fail due to NoClassDefFound
问题
首先,我想说明一下,我相对于Spring Boot和Spring Cloud还比较新手。
我有以下项目设置:
- 消费者驱动的契约存储在Git存储库中,按照这个指南。生产者必须回应两个访问两个不同端点的消费者,因此在这里没有冲突。
- 生产者也是按照相同的指南来获取契约的。
- 这两个项目都使用Maven进行设置。
生产者侧的依赖项包括:
- Spring Boot 3.0.2
- Spring Cloud Contract Verifier 4.0.1
- Spring Cloud Contract Maven插件 4.0.1
- Spring Security 6.0.1和Spring Security测试(虽然与问题无关,但可能很重要)
当使用spring-cloud-contract:generateTests
命令时,一切都正常运行,测试生成在适当的目录中,然后在运行生命周期命令test
时,运行生成的契约测试时产生以下错误:
NoClassDefFoundError: javax/servlet/Filter
at com.projectId.artifactId.contracts.ConsumerTest.validate_shouldReturnApplicationRoles(ConsumerTest.java:113)
projectId
,artifactId
,ConsumerTest
分别是实际包、构件名称和消费者名称的占位符。以下是失败的方法示例(所有生成的测试都失败并产生相同的错误,这种情况下的第113行是get("/auth/v1/role")
的地方):
public class ConsumerTest extends BaseTestClass {
@Test
public void validate_shouldReturnApplicationRoles() throws Exception {
// 给定:
MockMvcRequestSpecification request = given();
// 当:
ResponseOptions response = given().spec(request)
.get("/auth/v1/role");
// 那么:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).matches("application/json.*");
assertThat(response.header("Access-Control-Allow-Origin")).isEqualTo("*");
// 并且:
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);
}
}
测试配置文件仅配置了上下文路径和用于应用程序集成测试的h2实例。
我尝试了不同的测试基类,并期望测试能够通过,或者至少以有意义的方式失败。在这里出了什么问题?我怀疑Spring Security与这个错误有关...
更新
在只有Spring Boot和Spring Cloud Contract作为依赖项的空项目中,测试确实运行,但是如预期的那样失败(将期望的响应与实际响应进行比较)。BaseClass
在这种试验情况下稍有不同,因为这里只有一个模拟控制器。
更新 2
我已经将Spring Security添加到上述的模拟项目中,这足以使其无法编译。我尝试了两种BaseTestClass
定义,一种遵循Spring Cloud Contract的指南(这是之前有效的方法),另一种遵循Spring Security的指南在MockMvc
上,按照上述顺序如下:
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();
}
}
无论哪种方式,都无法运行应用程序,或是产生IllegalState
错误,或是spring test delegate cannot be null
错误。
英文:
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:
- Consumer Driven Contracts are stored in a Git repository following this guide. Producer has to answer to two consumers accessing two different endpoints, so there is no conflict there.
- Producer is setup following the same guide to fetch contracts from the remote.
- Both projects are set up using Maven.
Dependencies on the producer side are:
- Spring Boot 3.0.2
- Spring Cloud Contract Verifier 4.0.1
- Spring Cloud Contract Maven Plugin 4.0.1
- Spring Security 6.0.1 and Spring Security Test (unrelated but probably important)
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...
Update
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.
Update 2
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
.
答案1
得分: 0
这是您要翻译的代码部分:
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);
}
}
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论