Quand je me suis trop habitué à JUnit4 et que j'ai décidé d'écrire en JUnit5, j'ai dû enquêter un peu. Maintenant, je vais résumer moi-même les exemples d'implémentation fréquemment utilisés.
L'application Web à tester suppose l'API Rest et n'inclut pas le test de couche View.
Test du contrôleur Faire une pseudo requête avec MockMVC et tester Filter + Controller + ExceptionHandler
Test avec Mock --Mock le haricot et le tester
Faites une DI et l'autre Mock pour les tests
Test de paramétrage Répétez le même cas avec seulement les données de test modifiées
Tout d'abord, créez une application à tester. Comme d'habitude, Créé en ajoutant lombok, Spring Web et Validation à GradleProject avec spring initializr .RELEASE & packaging = jar & jvmVersion = 11 & groupId = com.example & artifactId = 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' //ajouter à
testAnnotationProcessor 'org.projectlombok:lombok' //ajouter à
}
test {
useJUnitPlatform()
}
Pour le build.gradle
généré, deux lignes ont été ajoutées à dependency
afin que lombok
puisse être utilisé dans le code de test. (Voir les commentaires ci-dessus)
Nous allons créer les classes suivantes pour ce projet.
Créez deux Service
s appelés depuis Controller
.
Tout d'abord, il effectue un traitement interne simple.
DemoService.java
@Service
public class DemoService {
//Je te saluerai
public String hello() {
return "hello";
}
//Je diviserai
public BigDecimal divide(BigDecimal a, BigDecimal b) {
return a.divide(b, 2, RoundingMode.HALF_UP);
}
}
L'autre consiste à acquérir une ressource externe (API Qiita).
ExternalService.java
@Service
@RequiredArgsConstructor
public class ExternalService {
private static final String EXTERNAL_RESOURCE_URL = "https://qiita.com/api/v2/schema";
private final RestTemplate restTemplate;
//Renvoie le résultat du schéma de l'API Qiita
public String getExternalResource() {
ResponseEntity<String> response =
restTemplate.exchange(EXTERNAL_RESOURCE_URL, HttpMethod.GET, null, String.class);
return response.getBody();
}
}
Créez une classe Configuration
pour DI le restTemplate
ci-dessus.
AppConfig.java
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Créez un contrôleur qui utilise les deux services mentionnés précédemment.
DemoController.java
@RestController
@RequiredArgsConstructor
@Validated
public class DemoController {
private final DemoService demoService;
private final ExternalService externalService;
//Je te saluerai
@GetMapping("/")
public CommonResponse hello() {
String data = demoService.hello();
return CommonResponse.builder().data(data).build();
}
//Je diviserai
@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();
}
//Renvoie le résultat du schéma de l'API Qiita
@GetMapping("/external")
public CommonResponse external() {
String data = externalService.getExternalResource();
return CommonResponse.builder().data(data).build();
}
}
Puisque la division vérifie l'entrée, nous mettrons en œuvre le cas de test plus tard de ce point de vue. Créez la classe de réponse comme suit.
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;
}
Nous allons également créer un filtre et un ExceptionHandler pour un exemple plus pratique. Ce filtre est aussi simple que la sortie d'un journal avant et après le traitement d'une demande.
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());
}
}
}
Créez également un ExceptionHandler. Il est utilisé pour gérer diverses erreurs communes.
CommonExceptionHandler.java
@RestControllerAdvice
@Slf4j
public class CommonExceptionHandler extends ResponseEntityExceptionHandler {
// 404:Gère les erreurs de ressource introuvable
//* Pour gérer cette application.Vous devez également définir des propriétés
@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:Gère les erreurs de vérification d'entrée
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<CommonResponse> handleValidationError(ConstraintViolationException e) {
//Séparez les éléments d'erreur d'entrée et les messages par des virgules(,)Ajouté à.
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:Je vais gérer d'autres erreurs inconnues
@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);
}
}
Placez les paramètres suivants dans ʻapplication.properties pour gérer
404: Resouce Not Found` dans le ExceptionHandler ci-dessus.
application.properties
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
Ceci termine la préparation de l'application à utiliser pour les tests. Les fichiers édités et créés jusqu'à présent ont la structure suivante.
├── 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
Les principaux rôles du contrôleur sont les suivants.
--Mappage de demande --Obtenir des paramètres
Étant donné que ces comportements dépendent de la fonctionnalité de Spring MVC, il n'a pas beaucoup de sens d'écrire du code de test pour la classe Controller uniquement.
Par conséquent, nous utiliserons MockMVC
pour effectuer un test qui reproduit le comportement de Spring MVC.
En ce qui concerne la demande acceptée par le contrôleur, non seulement le cas normal mais également la réponse du cas anormal supposé tel qu'une erreur de vérification d'entrée est vérifiée sans exception.
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) //Configurer Mock MVC
.addFilter(logFilter, "/*") //Cependant, le filtre doit être ajouté manuellement
.build();
}
//racine"/Je vais tester votre demande
@Test
void hello() throws Exception {
mockMvc.perform(get("/")) //racine"/Envoyez une pseudo requête à
.andExpect(status().isOk()) //HttpStatus est égal à 200:Être d'accord
.andExpect(jsonPath("$.status").value("success")) //La valeur de json est comme prévu
.andExpect(jsonPath("$.message").value("request succeeded.")) // 〃
.andExpect(jsonPath("$.data").value("hello"));
}
//Je vais tester la demande de division (10/3)
@Test
void divideSuccess() throws Exception {
mockMvc
.perform(get("/divide/10/3")) // 「/divide/10/Envoyer une pseudo requête à 3 "
.andExpect(status().isOk()) //HttpStatus est égal à 200:Être d'accord
.andExpect(jsonPath("$.status").value("success"))
.andExpect(jsonPath("$.message").value("request succeeded."))
.andExpect(jsonPath("$.data").value("3.33")); // 10 ÷ 3 = 3.Être 33
}
//Test des mauvaises requêtes (10 ÷ aaa)
@Test
void divideInvalidParameter() throws Exception {
mockMvc
.perform(get("/divide/10/aaa")) // 「/divide/10/Envoyer une pseudo requête à "aaa"
.andExpect(status().isBadRequest()) //HttpStatus est 400:Être une mauvaise demande
.andExpect(jsonPath("$.status").value("failure"))
.andExpect(jsonPath("$.message").value("divide.num2:must match \"[0-9]*\"")); //Il y a un message d'erreur
}
//Je vais tester une demande de division zéro (10 ÷ 0)
@Test
void divideZeroError() throws Exception {
mockMvc
.perform(get("/divide/10/0")) // 「/divide/10/Envoyer une pseudo requête à "0"
.andExpect(status().is5xxServerError()) //HttpStatus est 500:ServerError
.andExpect(jsonPath("$.status").value("failure"))
.andExpect(jsonPath("$.message").value("error has occurred."));
}
//Tester l'acquisition de ressources externes (API de schéma Qiita)
@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()) //Pas vide
.andReturn();
//Je vais afficher la réponse acquise dans le journal
log.info("external response : {}", mvcResult.getResponse().getContentAsString());
}
}
Le but est de configurer MockMvc
avec WebAppicationContext
. [^ 1]
En faisant cela, vous pouvez reproduire l'état qui est presque le même que le déploiement sur le serveur d'applications.
[^ 1]: MockMvc a également une autre configuration autonome
, et il existe également un mode approprié pour les tests unitaires qui peuvent être finement personnalisés tels que Controller, ControllerAdvice, Config à tester.
** Cependant, notez que vous devez spécifier uniquement Filter
avec ʻaddFilter (Filter," path ")` par vous-même. ** **
Le test précédent utilisait le contrôleur DI et le service.
Cependant, lors de l'utilisation d'une ressource externe telle que ʻExternalService`, il n'est pas bon que le test du contrôleur échoue en raison de l'état (l'autre serveur est arrêté et inaccessible, le Wi-Fi est désactivé, etc.). ..
** Utilisez Mock à de tels moments. ** **
Si vous vérifiez à nouveau le rôle de contrôleur,
Cependant, parmi ceux-ci, «4. Calling Business Logic (Service)» peut être ** simulé pour renvoyer la valeur attendue afin que vous puissiez vous concentrer sur le test du contrôleur **.
Convertissons la classe Service du test précédent en Mock.
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; //Convertir en maquette et enregistrer dans un conteneur DI
@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("Bonjour"); //Définissez d'abord la valeur de retour de la maquette
// request execute
mockMvc
.perform(get("/"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("success"))
.andExpect(jsonPath("$.message").value("request succeeded."))
.andExpect(jsonPath("$.data").value("Bonjour")); //Vérifiez en modifiant la valeur attendue
// verify
verify(demoService, times(1)).hello(); //Vérifiez le nombre d'appels simulés
}
@Test
void divideSuccess() throws Exception {
// mock
when(demoService.divide(any(), any())).thenReturn(new BigDecimal("3.33")); //Indépendamment de l'argument"3.33"rends le
// 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()); //Appel simulé validé 0 fois en raison d'une erreur d'entrée
}
@Test
void divideZeroError() throws Exception {
// mock
when(demoService.divide(any(), eq(BigDecimal.ZERO)))
.thenThrow(new ArithmeticException("/ by zero")); //Reproduisez l'erreur en supposant une division nulle
// 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)); //L'appel simulé valide une fois
}
@Test
void getExternalResource() throws Exception {
// mock
when(externalService.getExternalResource())
.thenReturn("this is mock data for internal test."); //Renvoyer le libellé sans accéder aux ressources externes
// 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();
//Je vais afficher la réponse acquise dans le journal
log.info("external response : {}", mvcResult.getResponse().getContentAsString());
// verify
verify(externalService, times(1)).getExternalResource(); //L'appel simulé valide une fois
}
}
Le plus important avec ce changement est que ʻExternalService` renvoie désormais des données factices sans accéder aux ressources externes.
Vous pouvez désormais exécuter des tests Controller indépendamment des ressources externes.
Dans le test précédent, DemoService
et ʻExternalService ont été Mocked, mais ** un seul peut être Mocked **. Par exemple, si vous souhaitez simuler uniquement ʻExternalService
, ce sera comme suit.
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; //Le service externe a un accès externe, alors faites-le simuler
@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."));
}
//Seule l'acquisition de ressources externes est vérifiée à l'aide de Mock.
@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();
}
}
Avec cela, vous pouvez librement DI le bean au moment du test ou en faire un Mock. Très pratique! !!
Enfin, je laisserai un exemple de la fonction pratique «test de paramétrage» ajoutée dans JUnit5 en utilisant le test unitaire de «Service» comme exemple. Il sera facile de répéter le même cas en ne modifiant que les données de test.
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());
}
// @Je vais tester la méthode de division avec divers modèles d'entrée avec ParameterizedTest
@ParameterizedTest
@MethodSource("divideTestArgs") // "devideTestArgs"J'utiliserai une méthode statique nommée
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;
//Exécution de la méthode d'exécution
try {
actual = demoService.divide(new BigDecimal(b1), new BigDecimal(b2));
} catch (Exception e) {
error = e;
}
//Valeur attendue et vérification
assertEquals(expect, actual);
//Vérifiez qu'aucune erreur ne s'est produite
assertEquals(hasError, error != null);
}
//diviser la liste des paramètres de test
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}, //Arrondi (arrondi au troisième chiffre de la virgule décimale)
new Object[] {"11", "3", "3.67", false}, //Arrondi (arrondi au troisième chiffre de la virgule décimale)
new Object[] {"1", "0", null, true}); //Diviser à zéro
}
}
En outre, MethodSource
Il semble que vous puissiez l'utiliser comme source de données.
À propos de la fonction de JUnit5 [cette page](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) était très facile à comprendre! J'ai été très utile mm
Il existe de nombreux articles sur le test de JUnit, mais il y a beaucoup d'articles sur les anciennes versions, donc j'espère que cela sera utile pour les débutants qui relèvent de Spring Boot et JUnit 5.
Recommended Posts