繁体   English   中英

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

[英]Endpoint testing with Spring Boot and JUnit 5

感觉很愚蠢,但我无法在 Spring Boot(版本2.7.1 )和 JUnit 5 中测试端点。

简而言之,我想测试真正的端点响应,所以我创建了一个测试 class,如测试 Web 层中所述 这里的代码:

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为空,我没有通过其他方式设置params),所以所有的配置值都是Spring的默认值。

可能这对傻瓜来说是个问题,但从昨天开始我一直在寻找解决方案,但我没有找到。

更多细节

Controller

@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);
  }

Gradle 构建

(这是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

我建议您使用org.springframework.test.web.servlet.MockMvc来测试您的端点。 您可以阅读本文以了解为什么应该使用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())//
    }
}

在此处此处阅读有关使用MockMvc进行测试的更多信息。

更新

还有另一种方法可以在您的测试 class 中启动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();
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM