Spring Cloud合同测试由于NoClassDefFound而失败

huangapple go评论77阅读模式
英文:

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)

projectIdartifactIdConsumerTest分别是实际包、构件名称和消费者名称的占位符。以下是失败的方法示例(所有生成的测试都失败并产生相同的错误,这种情况下的第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.

huangapple
  • 本文由 发表于 2023年3月7日 18:55:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/75661071.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定