When creating a new Web API with PJ, I investigated what kind of test can be created with Junit, so make a note.
Insert mock with mockito without depending on other classes. Testing is possible without waiting for the implementation of other classes.
EmployeeControllerSliceTest
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import com.example.demo.domain.Employee;
import com.example.demo.usecase.GetEmployeeUsecase;
@WebMvcTest(EmployeeController.class)
class EmployeeControllerSliceTest {
/**Mock to inject into test target*/
@MockBean
GetEmployeeUsecase mockedUsecase;
/**Client for connection test*/
@Autowired
private MockMvc mvc;
@Test
void testGetOne() throws Exception {
//Set mock movement
when(this.mockedUsecase.getEmployee(anyString())).thenReturn(Optional.ofNullable(new Employee("foo", "bar")));
//Run the test
this.mvc.perform(get("/employees/{employeeId}", "123")) //
.andExpect(status().is(200)) //
.andExpect(content().json("{\"employeeId\":\"foo\",\"name\":\"bar\"}")); //Mock data regardless of query
}
}
reference Auto-configured Spring MVC Tests Mocking and Spying Beans
Can be tested by explicitly declaring dependence on other classes.
EmployeeControllerSliceAndImportTest
package com.example.demo.presentation;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.web.servlet.MockMvc;
import com.example.demo.infrastructure.TempEmployeeRepositoryImp;
import com.example.demo.usecase.GetEmployeeUsecase;
@WebMvcTest(EmployeeController.class)
@Import({ GetEmployeeUsecase.class, TempEmployeeRepositoryImp.class })
class EmployeeControllerSliceAndImportTest {
/**Client for connection test*/
@Autowired
private MockMvc mvc;
@Test
void testGetOne() throws Exception {
//Run
this.mvc.perform(get("/employees/{employeeId}", "123")) //
//Verification
.andExpect(status().is(200)) //
.andExpect(content().json("{\"employeeId\":\"123\",\"name\":\"Taro\"}")); //Data obtained from Repository
}
}
reference Auto-configured Spring MVC Tests
Local testing is possible in an environment that is almost the same as when the server is actually started.
EmployeeControllerOnLocalhostTest
package com.example.demo.presentation;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class EmployeeControllerOnLocalhostTest {
/**Client for connection test*/
WebTestClient client = WebTestClient.bindToServer().build();
/**Connection destination port number*/
@LocalServerPort
private int port;
@Test
void test() throws Exception {
//Run
this.client.get().uri("http://localhost:" + this.port + "/employees/{employeeId}", "123").exchange() //
//Verification
.expectStatus().isEqualTo(HttpStatus.valueOf(200)) //
.expectBody(String.class).isEqualTo("{\"employeeId\":\"123\",\"name\":\"Taro\"}");
}
}
reference Testing with a running server WebTestClient Spring WebClient Requests with Parameters
You can connect to any running server and test its behavior. It is necessary to start the server separately before executing the Junit test. Conversely, you can test any running Web API server. It doesn't have to be Java.
EmployeeControllerOnExternalServerTest
package com.example.demo.presentation;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.reactive.server.WebTestClient;
class EmployeeControllerOnExternalServerTest {
/**Client for connection test*/
WebTestClient client = WebTestClient.bindToServer().build();
/**Connection destination port number*/
private final int port = 8080;
@Test
void test() throws Exception {
//Run
this.client.get().uri("http://localhost:" + this.port + "/employees/{employeeId}", "123").exchange() //
//Verification
.expectStatus().isEqualTo(HttpStatus.valueOf(200)) //
.expectBody(String.class).isEqualTo("{\"employeeId\":\"123\",\"name\":\"Taro\"}");
}
}
reference WebTestClient Spring WebClient Requests with Parameters
EmployeeController.java
EmployeeController
package com.example.demo.presentation;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.domain.Employee;
import com.example.demo.usecase.GetEmployeeUsecase;
@RestController
public class EmployeeController {
@Autowired
GetEmployeeUsecase getUsecase;
@GetMapping("/employees/{employeeId}")
Optional<Employee> getOne(@PathVariable final String employeeId) {
return this.getUsecase.getEmployee(employeeId);
}
}
GetEmployeeUsecase.java
GetEmployeeUsecase
package com.example.demo.usecase;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.domain.Employee;
import com.example.demo.domain.EmployeeRepository;
@Service
public class GetEmployeeUsecase {
@Autowired
EmployeeRepository repository;
public Optional<Employee> getEmployee(final String employeeId) {
return this.repository.findOneById(employeeId);
}
}
Employee.java
Employee
package com.example.demo.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class Employee {
private String employeeId = null;
private String name = null;
@SuppressWarnings("unused")
private Employee() {
}
}
EmployeeRepository.java
EmployeeRepository
package com.example.demo.domain;
import java.util.Optional;
import org.springframework.stereotype.Repository;
public interface EmployeeRepository {
Optional<Employee> findOneById(String employeeId);
}
TempEmployeeRepositoryImp.java
TempEmployeeRepositoryImp
package com.example.demo.infrastructure;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Repository;
import com.example.demo.domain.Employee;
import com.example.demo.domain.EmployeeRepository;
@Repository
public class TempEmployeeRepositoryImp implements EmployeeRepository {
private final List<Employee> db;
public TempEmployeeRepositoryImp() {
this.db = new ArrayList<>();
//As initial data
this.db.add(new Employee("123", "Taro"));
}
@Override
public Optional<Employee> findOneById(final String employeeId) {
return this.db.stream().filter(e -> e.getEmployeeId().equals(employeeId)).findFirst();
}
}
GitHub https://github.com/tmtmra/restControllerTestSample
I'm a beginner who started programming a year ago, so please give me various opinions and advice.
Recommended Posts