使用 Spring Boot 和 JUnit 5 进行 端点 测试

cczfrluj  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(203)

感觉很愚蠢,但我不能在Sping Boot (版本2.7.1)中使用JUnit 5测试端点。
简单地说,我想测试真实的的端点响应,所以我创建了一个测试类,如Testing the Web Layer中所述。

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApiDocumentationControllerIntegrationTest {

  @Autowired
  private TestRestTemplate restTemplate;

  @Test
  public void getOpenApiDocumentationShouldReturnOk() {
    assertThat(restTemplate.getForEntity("/api-docs", String.class).getStatusCode())
      .isEqualTo(HttpStatus.OK);
  }
}

但是当我运行测试时,TestRestTemplate调用http://localhost:8080/api-docs,忽略了服务器应该监听随机端口的事实。
我遗漏了什么?正如其他例子所示,我试图补充:

@LocalServerPort
  private int randomServerPort;

但在本例中我在启动测试时有一个例外:

java.lang.IllegalArgumentException: Could not resolve placeholder 'local.server.port' in value "${local.server.port}"

我尝试过将0设置为端口--框架应该将其视为随机端口--但没有成功。Spring抱怨它无法监听localhost:0
服务配置是空的(AKA application.yaml是空的,我没有以其他方式设置参数),所以所有的配置值都是Spring Boot的默认值。
也许这是一个问题的假人,但从昨天开始,我一直在寻找一个解决办法,但我没有找到它。

更多详细信息

控制器

@Slf4j
@Controller
public class ApiDocumentationController {

  private final Resource resourceFile;
  private final ObjectMapper yamlReader;
  private final ObjectMapper jsonWriter;

  public ApiDocumentationController(@Value("classpath:openapi/api-documentation.yaml") Resource resourceFile,
                                    @Qualifier("yamlReader") ObjectMapper yamlReader,
                                    ObjectMapper objectMapper) {
    this.resourceFile = resourceFile;
    this.yamlReader = yamlReader;
    this.jsonWriter = objectMapper;
  }

  @GetMapping(value = "/api-docs", produces = {MediaType.APPLICATION_JSON_VALUE})
  public ResponseEntity<String> getOpenApiDocumentation() {
    return Try.of(() -> yamlReader.readValue(resourceFile.getInputStream(), Object.class))
      .mapTry(jsonWriter::writeValueAsString)
      .map(apiDocumentation -> ResponseEntity.status(HttpStatus.OK).body(apiDocumentation))
      .get();  // FIXME This forced Try::get is ugly
  }
}

启动程序

@SpringBootApplication
public class AirportTravellersInsightsServiceApplication {

  public static void main(String[] args) {
    SpringApplication.run(AirportTravellersInsightsServiceApplication.class, args);
  }

分级构建

  • (这是build.gradle的摘录。)*

上述测试位于integrationTest源集中。

plugins {
  id 'org.springframework.boot' version '2.7.1'
  id 'io.spring.dependency-management' version '1.0.11.RELEASE'
  id 'java'
  id 'jacoco'
  id 'checkstyle'
  id 'idea'
}

group = 'example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
  mavenCentral()
}

ext.versions = [
  checkstyleVersion: "8.39",
  vavrVersion:       "0.10.4"
]

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-web'
  implementation 'org.projectlombok:lombok'
  annotationProcessor 'org.projectlombok:lombok'
  implementation "io.vavr:vavr:${versions.vavrVersion}"
  implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
  // TEST
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
  testImplementation 'org.springframework.security:spring-security-test'
  testImplementation 'org.junit.jupiter:junit-jupiter-api'
  testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine"
  testAnnotationProcessor 'org.projectlombok:lombok'
}

sourceSets {
  integrationTest {
    compileClasspath += sourceSets.main.output
    compileClasspath += sourceSets.test.output
    runtimeClasspath += sourceSets.main.output
    runtimeClasspath += sourceSets.test.output
  }
}

configurations {
  compileOnly {
    extendsFrom annotationProcessor
  }
  integrationTestImplementation.extendsFrom testImplementation
  integrationTestRuntimeOnly.extendsFrom runtimeOnly
  implementation {
    exclude module: 'spring-boot-starter-tomcat'
  }
}

tasks.named('test') {
  useJUnitPlatform()
}

task integrationTest(type: Test, description: 'Runs integration tests.', group: LifecycleBasePlugin.VERIFICATION_GROUP) {
  useJUnitPlatform()
  testClassesDirs = sourceSets.integrationTest.output.classesDirs
  classpath = sourceSets.integrationTest.runtimeClasspath
  shouldRunAfter test
}
check.dependsOn integrationTest
zpjtge22

zpjtge221#

我建议您使用org.springframework.test.web.servlet.MockMvc来测试您的端点。您可以阅读this来了解为什么应该使用MockMvc
MockMvc提供对Spring MVC测试的支持。它封装所有Web应用程序Bean并使它们可用于测试。
您可以通过以下方式执行测试:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class ApiDocumentationControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void getOpenApiDocumentationShouldReturnOk() {
        // Act
        ResultActions response = mockMvc.perform(//
                get("/api-docs"));

        // Assert
        response.andDo(print()).//
                andExpect(status().isOk())//
    }
}

了解更多有关使用MockMvcherehere进行测试的信息。

更新

还有另一种方法可以在测试类中启动MockMvc示例:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApiDocumentationControllerIntegrationTest {

    @Autowired
    private WebApplicationContext webApplicationContext;
    private MockMvc mockMvc;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

}

相关问题