[JAVA] Ich habe jetzt einen Test mit Spring Boot + JUnit 5 geschrieben

Einführung

Als ich mich zu sehr an JUnit4 gewöhnt hatte und mich entschied, in JUnit5 zu schreiben, musste ich einiges untersuchen. Jetzt werde ich die häufig verwendeten Implementierungsbeispiele für mich zusammenfassen.

Die zu testende Webanwendung setzt die Rest-API voraus und enthält den View-Layer-Test nicht.

In diesem Artikel behandeltes Implementierungsbeispiel

Entwicklungsumgebung

1. Vorbereitung der zu prüfenden Anwendung

Erstellen Sie zunächst eine zu testende App. Wie üblich Erstellt durch Hinzufügen von Lombok, Spring Web und Validation zu GradleProject mit Spring Initializr .RELEASE & package = jar & jvmVersion = 11 & groupId = com.example & artefaktId = demo & name = demo & description = Demo% 20project% 20for% 20Spring% 20Boot & packageName = com.example.demo & dependencies = lombok, web, validation).

build.gradle


plugins {
	id 'org.springframework.boot' version '2.3.4.RELEASE'
	id 'io.spring.dependency-management' version '1.0.10.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
	testCompileOnly 'org.projectlombok:lombok'  //hinzufügen
	testAnnotationProcessor 'org.projectlombok:lombok'  //hinzufügen
}

test {
	useJUnitPlatform()
}

Für das generierte "build.gradle" wurden zwei Zeilen zu "dependency" hinzugefügt, damit "lombok" im Testcode verwendet werden kann. (Siehe Kommentare oben) Wir werden die folgenden Klassen für dieses Projekt erstellen.

1-1. Erstellen von Service und Konfiguration

Erstellen Sie zwei "Services", die von "Controller" aufgerufen werden. Zunächst führt es eine einfache interne Verarbeitung durch.

DemoService.java


@Service
public class DemoService {
  //Ich werde dich begrüßen
  public String hello() {
    return "hello";
  }
  //Ich werde teilen
  public BigDecimal divide(BigDecimal a, BigDecimal b) {
    return a.divide(b, 2, RoundingMode.HALF_UP);
  }
}

Die andere besteht darin, eine externe Ressource (Qiita-API) zu erwerben.

ExternalService.java


@Service
@RequiredArgsConstructor
public class ExternalService {
  private static final String EXTERNAL_RESOURCE_URL = "https://qiita.com/api/v2/schema";
  private final RestTemplate restTemplate;
  //Gibt das Ergebnis des Schemas der Qiita-API zurück
  public String getExternalResource() {
    ResponseEntity<String> response =
        restTemplate.exchange(EXTERNAL_RESOURCE_URL, HttpMethod.GET, null, String.class);
    return response.getBody();
  }
}

Erstellen Sie eine "Configuration" -Klasse, um die oben genannte "restTemplate" zu erstellen.

AppConfig.java


@Configuration
public class AppConfig {
  @Bean
  public RestTemplate restTemplate() {
    return new RestTemplate();
  }
}

1-2. Erstellen eines Controllers

Erstellen Sie einen Controller, der die beiden zuvor genannten Dienste verwendet.

DemoController.java


@RestController
@RequiredArgsConstructor
@Validated
public class DemoController {

  private final DemoService demoService;
  private final ExternalService externalService;

  //Ich werde dich begrüßen
  @GetMapping("/")
  public CommonResponse hello() {
    String data = demoService.hello();
    return CommonResponse.builder().data(data).build();
  }

  //Ich werde teilen
  @GetMapping("/divide/{num1}/{num2}")
  public CommonResponse divide(
      @PathVariable @Pattern(regexp = "[0-9]*") String num1,
      @PathVariable @Pattern(regexp = "[0-9]*") String num2) {
    BigDecimal data = demoService.divide(new BigDecimal(num1), new BigDecimal(num2));
    return CommonResponse.builder().data(data).build();
  }

  //Gibt das Ergebnis des Schemas der Qiita-API zurück
  @GetMapping("/external")
  public CommonResponse external() {
    String data = externalService.getExternalResource();
    return CommonResponse.builder().data(data).build();
  }
}

Da die Abteilung die Eingabe überprüft, werden wir den Testfall später unter diesem Gesichtspunkt implementieren. Erstellen Sie die Antwortklasse wie folgt.

CommonResponse.java


@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CommonResponse<T> {
    @Builder.Default
    private String status = "success";
    @Builder.Default
    private String message = "request succeeded.";
    private T data;
}

1-3. Erstellen von Filter und ExceptionHandler

Wir werden auch einen Filter und einen ExceptionHandler für ein praktischeres Beispiel erstellen. Dieser Filter ist so einfach wie die Ausgabe eines Protokolls vor und nach der Verarbeitung einer Anforderung.

LogFilter.java


@Component
@Slf4j
public class LogFilter implements Filter {
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    log.info("[IN]{}:{}", req.getMethod(), req.getRequestURI());
    try {
      chain.doFilter(request, response);
    } finally {
      log.info("[OUT]{}:{}", req.getMethod(), req.getRequestURI());
    }
  }
}

Erstellen Sie auch einen ExceptionHandler. Es wird verwendet, um verschiedene Fehler gemeinsam zu behandeln.

CommonExceptionHandler.java


@RestControllerAdvice
@Slf4j
public class CommonExceptionHandler extends ResponseEntityExceptionHandler {

  // 404:Behandelt Fehler "Ressource nicht gefunden"
  //* Um diese Anwendung zu behandeln.Sie müssen auch Eigenschaften festlegen
  @Override
  protected ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
    ServletWebRequest req = (ServletWebRequest)request;
    log.warn("resource not found. {}", req.getRequest().getRequestURI());
    return new ResponseEntity(
            CommonResponse.builder().status("failure").message("resource not found.").build(),
            HttpStatus.NOT_FOUND);
  }

  // 400:Behandelt Eingabeprüfungsfehler
  @ExceptionHandler(ConstraintViolationException.class)
  public ResponseEntity<CommonResponse> handleValidationError(ConstraintViolationException e) {
    //Trennen Sie Eingabefehlerelemente und Meldungen durch Kommas(,)Hinzugefügt zu.
    String validationErrorMessages =
        e.getConstraintViolations().stream()
            .map(cv -> cv.getPropertyPath().toString() + ":" + cv.getMessage())
            .collect(Collectors.joining(", "));
    log.info("Bad request. {}", validationErrorMessages);
    return new ResponseEntity<>(
        CommonResponse.builder().status("failure").message(validationErrorMessages).build(),
        HttpStatus.BAD_REQUEST);
  }

  // 500:Ich werde andere unbekannte Fehler behandeln
  @ExceptionHandler
  public ResponseEntity<CommonResponse> handleException(Exception e) {
    log.error("Request failed.", e);
    return new ResponseEntity<>(
        CommonResponse.builder().status("failure").message("error has occurred.").build(),
        HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

Fügen Sie die folgenden Einstellungen in application.properties ein, um 404: Resouce Not Found im obigen ExceptionHandler zu behandeln.

application.properties


spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

1-4. Struktur der erstellten Datei

Damit ist die Vorbereitung der zum Testen zu verwendenden Anwendung abgeschlossen. Die bis zu diesem Zeitpunkt bearbeiteten und erstellten Dateien haben die folgende Struktur.

├── build.gradle
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com.example.demo
│   │   │               ├── AppConfig.java
│   │   │               ├── DemoApplication.java
│   │   │               ├── controller
│   │   │               │   ├── CommonExceptionHandler.java
│   │   │               │   ├── CommonResponse.java
│   │   │               │   └── DemoController.java
│   │   │               ├── filter
│   │   │               │   └── LogFilter.java
│   │   │               └── service
│   │   │                   ├── DemoService.java
│   │   │                   └── ExternalService.java
│   │   └── resources
│   │       └── application.properties
│   └── test
└── web

2. Controller-Test

Die Hauptrollen von Controller sind wie folgt.

Da diese Verhaltensweisen von der Funktionalität von Spring MVC abhängen, ist es wenig sinnvoll, Testcode nur für die Controller-Klasse zu schreiben. Daher werden wir "MockMVC" verwenden, um einen Test durchzuführen, der das Verhalten von Spring MVC reproduziert.

2-1. Beispieltestfall mit MockMVC

In Bezug auf die vom Controller akzeptierte Anforderung wird nicht nur der Normalfall, sondern auch die Antwort des angenommenen abnormalen Falls, wie z. B. ein Eingabeprüffehler, ausnahmslos überprüft.

DemoControllerTest.java


package com.example.demo.controller;

import com.example.demo.filter.LogFilter;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@EnableWebMvc
@Slf4j
public class DemoControllerTest {

  MockMvc mockMvc;

  @Autowired WebApplicationContext webApplicationContext;
  @Autowired LogFilter logFilter;

  @BeforeEach
  void beforeEach() {
    mockMvc =
        MockMvcBuilders.webAppContextSetup(webApplicationContext)     //Richten Sie Mock MVC ein
            .addFilter(logFilter, "/*")                               //Der Filter muss jedoch manuell hinzugefügt werden
            .build();
  }

  //Wurzel"/Ich werde Ihre Anfrage testen
  @Test
  void hello() throws Exception {
    mockMvc.perform(get("/"))                                         //Wurzel"/Senden Sie eine Pseudoanfrage an
        .andExpect(status().isOk())                                   //HttpStatus ist 200:Okay sein
        .andExpect(jsonPath("$.status").value("success"))             //Der Wert von json ist wie erwartet
        .andExpect(jsonPath("$.message").value("request succeeded.")) // 〃
        .andExpect(jsonPath("$.data").value("hello"));
  }

  //Ich werde die Teilungsanfrage testen (10/3)
  @Test
  void divideSuccess() throws Exception { 
    mockMvc
        .perform(get("/divide/10/3"))                                 // 「/divide/10/Pseudoanfrage an 3 "senden
        .andExpect(status().isOk())                                   //HttpStatus ist 200:Okay sein
        .andExpect(jsonPath("$.status").value("success"))
        .andExpect(jsonPath("$.message").value("request succeeded."))
        .andExpect(jsonPath("$.data").value("3.33"));                 // 10 ÷ 3 = 3.Sei 33
  }
  
  //Test auf schlechte Anfragen (10 ÷ aaa)
  @Test
  void divideInvalidParameter() throws Exception {
    mockMvc
        .perform(get("/divide/10/aaa"))                               // 「/divide/10/Senden Sie eine Pseudoanfrage an "aaa"
        .andExpect(status().isBadRequest())                           //HttpStatus ist 400:Ein BadRequest sein
        .andExpect(jsonPath("$.status").value("failure"))
        .andExpect(jsonPath("$.message").value("divide.num2:must match \"[0-9]*\"")); //Es liegt eine Fehlermeldung vor
  }

  //Ich werde eine Anfrage für die Nullteilung testen (10 ÷ 0)
  @Test
  void divideZeroError() throws Exception {
    mockMvc
        .perform(get("/divide/10/0"))                                   // 「/divide/10/Pseudoanforderung an "0" senden
        .andExpect(status().is5xxServerError())                         //HttpStatus ist 500:ServerError
        .andExpect(jsonPath("$.status").value("failure"))
        .andExpect(jsonPath("$.message").value("error has occurred."));
  }
  
  //Testen Sie die Erfassung externer Ressourcen (Qiita-Schema-API)
  @Test
  void getExternalResource() throws Exception {
    MvcResult mvcResult =
        mockMvc
            .perform(get("/external"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.status").value("success"))
            .andExpect(jsonPath("$.message").value("request succeeded."))
            .andExpect(jsonPath("$.data").isNotEmpty())                //Nicht leer
            .andReturn();
    //Ich werde die erfasste Antwort auf das Protokoll ausgeben
    log.info("external response : {}", mvcResult.getResponse().getContentAsString());
  }
}

Der Punkt ist, MockMvc mit WebAppicationContext einzurichten. [^ 1] Auf diese Weise können Sie den Status reproduzieren, der fast dem Bereitstellen auf dem Anwendungsserver entspricht.

[^ 1]: MockMvc verfügt auch über ein anderes eigenständiges Setup, und es gibt auch einen Modus, der für Unit-Tests geeignet ist und genau angepasst werden kann, z. B. Controller, ControllerAdvice, Config zum Testen.

** Beachten Sie jedoch, dass Sie nur Filter mit addFilter (Filter," path ") angeben müssen. ** ** **

3. Testen mit Mock

Der vorherige Test verwendete DI Controller and Service.

Wenn Sie jedoch eine externe Ressource wie "ExternalService" verwenden, ist es nicht gut, dass der Controller-Test aufgrund des Status fehlschlägt (der andere Server ist ausgefallen und nicht erreichbar, Wi-Fi ist ausgeschaltet usw.). ..

** Verwenden Sie zu solchen Zeiten Mock. ** ** **

3-1 Verspotten Sie die Bohne und testen Sie sie

Wenn Sie die Rolle des Controllers erneut überprüfen,

  1. Fordern Sie die Zuordnung an
  2. Parameter abrufen
  3. Eingabeprüfung
  4. Aufrufen der Geschäftslogik (Service)
  5. Rückgabe der Antwort

Von diesen kann jedoch "4. Calling Business Logic (Service)" verspottet werden, um den erwarteten Wert zurückzugeben, sodass Sie sich auf das Testen des Controllers konzentrieren können.

Lassen Sie uns die Service-Klasse des vorherigen Tests in Mock konvertieren.

DemoControllerWithMockTest.java


package com.example.demo.controller;

import com.example.demo.filter.LogFilter;
import com.example.demo.service.DemoService;
import com.example.demo.service.ExternalService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import java.math.BigDecimal;

import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@EnableWebMvc
@Slf4j
class DemoControllerWithMockTest {

  MockMvc mockMvc;

  @Autowired WebApplicationContext webApplicationContext;
  @Autowired LogFilter logFilter;

  @MockBean DemoService demoService;         //In Mock konvertieren und im DI-Container registrieren
  @MockBean ExternalService externalService; // 〃

  @BeforeEach
  void beforeEach() {
    MockitoAnnotations.initMocks(this);
    mockMvc =
        MockMvcBuilders.webAppContextSetup(webApplicationContext)
            .addFilter(logFilter, "/*")
            .build();
  }

  @AfterEach
  void afterEach() {}

  @Test
  void hello() throws Exception {
    // mock
    when(demoService.hello()).thenReturn("Hallo");      //Stellen Sie zuerst den Rückgabewert des Mocks ein
    // request execute
    mockMvc
        .perform(get("/"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.status").value("success"))
        .andExpect(jsonPath("$.message").value("request succeeded."))
        .andExpect(jsonPath("$.data").value("Hallo"));  //Überprüfen Sie dies, indem Sie den erwarteten Wert ändern
    // verify
    verify(demoService, times(1)).hello();                 //Überprüfen Sie die Anzahl der Scheinanrufe
  }

  @Test
  void divideSuccess() throws Exception {
    // mock
    when(demoService.divide(any(), any())).thenReturn(new BigDecimal("3.33")); //Unabhängig vom Argument"3.33"Gib es zurück
    // request execute
    mockMvc
        .perform(get("/divide/10/3"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.status").value("success"))
        .andExpect(jsonPath("$.message").value("request succeeded."))
        .andExpect(jsonPath("$.data").value("3.33"));
    // verify
    verify(demoService, times(1)).divide(any(), any());
  }

  @Test
  void divideInvalidParameter() throws Exception {
    // request execute
    mockMvc
        .perform(get("/divide/10/aaa"))
        .andExpect(status().isBadRequest())
        .andExpect(jsonPath("$.status").value("failure"))
        .andExpect(jsonPath("$.message").value("divide.num2:must match \"[0-9]*\""));
    // verify
    verify(demoService, times(0)).divide(any(), any());  //Der Mock-Aufruf wurde aufgrund eines Eingabefehlers 0-mal validiert
  }

  @Test
  void divideZeroError() throws Exception {
    // mock
    when(demoService.divide(any(), eq(BigDecimal.ZERO)))
        .thenThrow(new ArithmeticException("/ by zero"));              //Reproduzieren Sie den Fehler unter der Annahme einer Nullteilung
    // request execute
    mockMvc
        .perform(get("/divide/10/0"))
        .andExpect(status().is5xxServerError())
        .andExpect(jsonPath("$.status").value("failure"))
        .andExpect(jsonPath("$.message").value("error has occurred."));
    // verify
    verify(demoService, times(1)).divide(any(), eq(BigDecimal.ZERO));  //Scheinanruf wird einmal validiert
  }

  @Test
  void getExternalResource() throws Exception {
    // mock
    when(externalService.getExternalResource())
        .thenReturn("this is mock data for internal test.");    //Geben Sie den Wortlaut zurück, ohne auf externe Ressourcen zuzugreifen
    // request execute
    MvcResult mvcResult =
        mockMvc
            .perform(get("/external"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.status").value("success"))
            .andExpect(jsonPath("$.message").value("request succeeded."))
            .andExpect(jsonPath("$.data").value("this is mock data for internal test."))
            .andReturn();
    //Ich werde die erfasste Antwort auf das Protokoll ausgeben
    log.info("external response : {}", mvcResult.getResponse().getContentAsString());
    // verify
    verify(externalService, times(1)).getExternalResource();  //Scheinanruf wird einmal validiert
  }
}

Am wichtigsten bei dieser Änderung ist, dass "ExternalService" jetzt Scheindaten zurückgibt, ohne auf externe Ressourcen zuzugreifen.

Sie können jetzt Controller-Tests unabhängig von externen Ressourcen ausführen.

3-2. Testen Sie einen mit DI und den anderen mit Mock

Im vorherigen Test wurden sowohl "DemoService" als auch "ExternalService" verspottet, aber ** nur einer kann verspottet werden **. Wenn Sie beispielsweise nur "ExternalService" verspotten möchten, ist dies wie folgt.

DemoControllerWithOneSideMockTest.java


package com.example.demo.controller;

import com.example.demo.filter.LogFilter;
import com.example.demo.service.ExternalService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@EnableWebMvc
@Slf4j
class DemoControllerWithOneSideMockTest {

  MockMvc mockMvc;

  @Autowired WebApplicationContext webApplicationContext;
  @Autowired LogFilter logFilter;

  @MockBean ExternalService externalService;    //Externer Dienst hat externen Zugriff, also machen Sie es verspotten

  @BeforeEach
  void beforeEach() {
    MockitoAnnotations.initMocks(this);
    mockMvc =
        MockMvcBuilders.webAppContextSetup(webApplicationContext)
            .addFilter(logFilter, "/*")
            .build();
  }

  @AfterEach
  void afterEach() {}

  @Test
  void hello() throws Exception {
    mockMvc
        .perform(get("/"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.status").value("success"))
        .andExpect(jsonPath("$.message").value("request succeeded."))
        .andExpect(jsonPath("$.data").value("hello"));
  }

  @Test
  void divideSuccess() throws Exception {
    mockMvc
        .perform(get("/divide/10/3"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.status").value("success"))
        .andExpect(jsonPath("$.message").value("request succeeded."))
        .andExpect(jsonPath("$.data").value("3.33"));
  }

  @Test
  void divideInvalidParameter() throws Exception {
    mockMvc
        .perform(get("/divide/10/aaa"))
        .andExpect(status().isBadRequest())
        .andExpect(jsonPath("$.status").value("failure"))
        .andExpect(jsonPath("$.message").value("divide.num2:must match \"[0-9]*\""));
  }

  @Test
  void divideZeroError() throws Exception {
    mockMvc
        .perform(get("/divide/10/0"))
        .andExpect(status().is5xxServerError())
        .andExpect(jsonPath("$.status").value("failure"))
        .andExpect(jsonPath("$.message").value("error has occurred."));
  }

  //Nur der Erwerb externer Ressourcen wird mit Mock überprüft.
  @Test
  void getExternalResource() throws Exception {
    // mock
    when(externalService.getExternalResource()).thenReturn("this is mock data for internal test.");
    // request
    MvcResult mvcResult =
        mockMvc
            .perform(get("/external"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.status").value("success"))
            .andExpect(jsonPath("$.message").value("request succeeded."))
            .andExpect(jsonPath("$.data").value("this is mock data for internal test."))
            .andReturn();
    //
    log.info("external response : {}", mvcResult.getResponse().getContentAsString());
    // verify
    verify(externalService, times(1)).getExternalResource();
  }
}

Mit dieser Option können Sie die Bohne zum Zeitpunkt des Tests frei DI oder zu einem Mock machen. Sehr angenehm! !!

4. Parametrisierungstest

Schließlich werde ich ein Beispiel der in JUnit5 hinzugefügten Komfortfunktion "Parametrisierungstest" am Beispiel des Komponententests "Service" hinterlassen. Es ist einfach, denselben Fall zu wiederholen, indem nur die Testdaten geändert werden.

DemoServiceTest.java


package com.example.demo.service;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringBootTest
class DemoServiceTest {

  @Autowired DemoService demoService;

  @Test
  void hello() {
    assertEquals("hello", demoService.hello());
  }

  // @Ich werde die Teilungsmethode mit verschiedenen Eingabemustern mit ParameterizedTest testen
  @ParameterizedTest
  @MethodSource("divideTestArgs")  // "devideTestArgs"Ich werde eine statische Methode namens verwenden
  void divide(String b1, String b2, String strExpect, boolean hasError) {

    BigDecimal expect = Optional.ofNullable(strExpect).map(BigDecimal::new).orElse(null);
    BigDecimal actual = null;
    Exception error = null;
    //Ausführung der Ausführungsmethode
    try {
      actual = demoService.divide(new BigDecimal(b1), new BigDecimal(b2));
    } catch (Exception e) {
      error = e;
    }
    //Erwarteter Wert und Überprüfung
    assertEquals(expect, actual);
    //Stellen Sie sicher, dass kein Fehler aufgetreten ist
    assertEquals(hasError, error != null);
  }

  //Testparameterliste teilen
  static List<Object[]> divideTestArgs() {
    return List.of(
        new Object[] {"1", "1", "1.00", false},
        new Object[] {"0", "1", "0.00", false},
        new Object[] {"5", "2", "2.50", false},
        new Object[] {"10", "3", "3.33", false}, //Rundung (Abrundung der dritten Ziffer des Dezimalpunkts)
        new Object[] {"11", "3", "3.67", false}, //Rundung (Aufrunden der dritten Stelle des Dezimalpunkts)
        new Object[] {"1", "0", null, true}); //Teilen Sie auf Null
  }
}

Außerdem MethodSource

Es scheint, dass Sie beispielsweise als Datenquelle verwenden können.

Informationen zur Funktion von JUnit5 [diese Seite](https://qiita.com/opengl-8080/items/efe54204e25f615e322f#%E3%83%91%E3%83%A9%E3%83%A1%E3%83% BC% E3% 82% BF% E5% 8C% 96% E3% 83% 86% E3% 82% B9% E3% 83% 88) war sehr einfach zu verstehen! Ich war sehr hilfreich mm

Nachwort

Es gibt viele Artikel zum Testen von JUnit, aber es gibt auch einige Artikel zu älteren Versionen. Ich hoffe, dass dies für Anfänger hilfreich ist, die sich mit Spring Boot und JUnit 5 herausfordern.

Recommended Posts

Ich habe jetzt einen Test mit Spring Boot + JUnit 5 geschrieben
[JUnit 5-kompatibel] Schreiben Sie einen Test mit JUnit 5 mit Spring Boot 2.2, 2.3
Ich habe GraphQL mit Spring Boot ausprobiert
Ich habe Flyway mit Spring Boot ausprobiert
Testen Sie den Spring Framework Controller mit Junit
Ich habe einen CRUD-Test mit SpringBoot + MyBatis + DBUnit geschrieben (Teil 1)
Erstellen Sie ein Eltern-Kind-Beziehungsformular mit form_object (ich habe auch einen Test geschrieben)
Führen Sie einen Transaktionsbestätigungstest mit Spring Boot durch
Ich habe ein einfaches Suchformular mit Spring Boot + GitHub Search API erstellt.
Beispielcode zum Testen eines Spring Boot-Controllers mit MockMvc
Ich habe Lazy Initialization mit Spring Boot 2.2.0 ausprobiert
Formularklassenvalidierungstest mit Spring Boot
[Spring Boot] Ich bin auf einen Methodenaufruf-Count-Test gestoßen (Spock-Framework)
[LINE BOT] Ich habe einen Ramen BOT mit Java (Maven) + Heroku + Spring Boot (1) gemacht.
Erstellen Sie eine Website mit Spring Boot + Gradle (jdk1.8.x)
Testen Sie den Controller mit Mock MVC im Spring Boot
Erstellen Sie mit Spring Boot eine einfache Such-App
Ich habe ein Programm zur Beurteilung von Primzahlen in Java geschrieben
[Java] Hallo Welt mit Java 14 x Spring Boot 2.3 x JUnit 5 ~
Ich wollte Spring Boot in einem Multiprojekt gradle
Erstellen Sie einen Web-API-Server mit Spring Boot
Erstellen Sie mit Docker eine Spring Boot-Entwicklungsumgebung
Machen Sie einen Unit-Test mit Junit.
Mit Spring Boot herunterladen
So führen Sie UT mit Excel als Testdaten mit Spring Boot + JUnit5 + DBUnit durch
Ich habe versucht, eine Webanwendung voller Fehler mit Spring Boot zu klonen
[RSpec] Ich habe einen Test zum Hochladen eines Profilbilds geschrieben.
So schreiben Sie einen Komponententest für Spring Boot 2
Implementieren Sie eine einfache Rest-API mit Spring Security mit Spring Boot 2.0
Ein Memorandum beim Erstellen eines REST-Service mit Spring Boot
Erstellen Sie mit Spring Security 2.1 eine einfache Demo-Site mit Spring Security
Wovon ich süchtig war, als ich eine Spring Boot-Anwendung mit VS Code entwickelte
Ich habe einen Testcode (Junit & mockit) für den Code geschrieben, der die AWS-API (Java) aufruft.
Beachten Sie, dass ich süchtig nach Stapelverarbeitung mit Spring Boot war
Generieren Sie mit Spring Boot einen Barcode
Hallo Welt mit Spring Boot
Testen Sie die Web-API mit junit
Beginnen Sie mit Spring Boot
Hallo Welt mit Spring Boot!
Führen Sie LIFF mit Spring Boot aus
SNS-Login mit Spring Boot
Spring Boot beginnend mit Docker
Hallo Welt mit Spring Boot
Setzen Sie Cookies mit Spring Boot
Verwenden Sie Spring JDBC mit Spring Boot
Modul mit Spring Boot hinzufügen
Erste Schritte mit Spring Boot
Erstellen Sie mit Spring Boot einen Mikrodienst
Mail mit Spring Boot verschicken
Ich habe das Spring Boot-Einführungshandbuch [Zugriff auf Daten mit JPA] ausprobiert.
Eine Geschichte voller Grundlagen von Spring Boot (gelöst)
Erstellen wir eine einfache API mit EC2 + RDS + Spring Boot ①
Ich habe Randoop untersucht, einen JUnit-Testklassengenerator für Java.
Ich habe versucht, mit Swagger mit Spring Boot zu beginnen
Implementieren Sie eine einfache Rest-API mit Spring Security & JWT mit Spring Boot 2.0
Verwenden Sie Spring Test + Mockito + JUnit 4 für Spring Boot + Spring Retry Unit Test
Ich habe mit Spring Boot ein einfaches MVC-Beispielsystem erstellt
[Java] Ich möchte Standardeingabe und Standardausgabe mit JUnit testen
Implementieren Sie einen einfachen Web-REST-API-Server mit Spring Boot + MySQL
Ich habe das Spring Boot-Einführungshandbuch [Erstellen eines RESTful-Webdiensts] ausprobiert.
Verwenden Sie die Standardauthentifizierung mit Spring Boot