Web/spring

[Spring Test] Testing in Spring Boot

태애니 2023. 4. 2. 18:17
728x90

https://www.baeldung.com/spring-boot-testing

 

Testing in Spring Boot | Baeldung

Learn about how the Spring Boot supports testing, to write unit tests efficiently.

www.baeldung.com

 

 

a typical tiered architecture — the API call is processed from the Controller to Service to the Persistence layer

 

 

 


Test Configuration With @TestConfiguration

a test annotated with @SpringBootTest will bootstrap the full application context, which means we can @Autowire any bean that's picked up by component scanning into our test

 

@RunWith(SpringRunner.class)
@SpringBootTest
public class EmployeeServiceImplIntegrationTest {

    @Autowired
    private EmployeeService employeeService;

    // class code ...
}

 

We can achieve this with the @TestConfiguration annotation. There are two ways of using the annotation. Either on a static inner class in the same test class where we want to @Autowire the bean

실제 애플리케이션 context가 아닌 테스트 구성을 사용할 수 있는 방법이 두가지 있다

 

1. static class 사용

@RunWith(SpringRunner.class)
public class EmployeeServiceImplIntegrationTest {

    @TestConfiguration
    static class EmployeeServiceImplTestContextConfiguration {
        @Bean
        public EmployeeService employeeService() {
            return new EmployeeService() {
                // implement methods
            };
        }
    }

    @Autowired
    private EmployeeService employeeService;
}

 

2. 별도 테스트 구성 클래스 사용

@TestConfiguration
public class EmployeeServiceImplTestContextConfiguration {
    
    @Bean
    public EmployeeService employeeService() {
        return new EmployeeService() { 
            // implement methods 
        };
    }
}

 

@TestConfiguration 애노테이션이 달린 구성 클래스는 구성 요소 스캔에서 제외되므로

명시적으로 @Autowire  하기 위해 등록해야한다

@Import 주석을 사용하여 이를 수행할 수 있다

@RunWith(SpringRunner.class)
@Import(EmployeeServiceImplTestContextConfiguration.class)
public class EmployeeServiceImplIntegrationTest {

    @Autowired
    private EmployeeService employeeService;

    // remaining class code
}

 

 

Mocking With @MockBean

Our Service layer code is dependent on our Repository

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Override
    public Employee getEmployeeByName(String name) {
        return employeeRepository.findByName(name);
    }
}

 

Service 계층을 테스트에  persistence layer를 신경 쓸 필요가 없다

이는 Spring Boot Test에서 제공하는 mock 를 사용하면 된다

 

@RunWith(SpringRunner.class)
public class EmployeeServiceImplIntegrationTest {

    @TestConfiguration
    static class EmployeeServiceImplTestContextConfiguration {
 
        @Bean
        public EmployeeService employeeService() {
            return new EmployeeServiceImpl();
        }
    }

    @Autowired
    private EmployeeService employeeService;

    @MockBean
    private EmployeeRepository employeeRepository;

    // write test cases here
}

Service Class를 확인하려면 테스트 클래스에서 @Autowire 할 수 있도록 @Bean으로 생성되고, 사용 가능한 Service 클래스의 인스턴스가 있어야한다. @TestConfiguration 을 사용하면 된다.

그리고 @MockBean을 사용하면 된다. 실제 EmployeeRespository 호출을 우회하지만 사용할 수 있도록 Mock을 생성해준다

 

@Before
public void setUp() {
    Employee alex = new Employee("alex");

    Mockito.when(employeeRepository.findByName(alex.getName()))
      .thenReturn(alex);
}

 

설정 후 테스트코드 작성을 하면 더 간단해진다

 

@Test
public void whenValidName_thenEmployeeShouldBeFound() {
    String name = "alex";
    Employee found = employeeService.getEmployeeByName(name);
 
     assertThat(found.getName())
      .isEqualTo(name);
 }

 

 

 

Integration Testing With @DataJpaTest

an entity named Employee, which has an id and a name as its properties :

@Entity
@Table(name = "person")
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Size(min = 3, max = 20)
    private String name;

    // standard getters and setters, constructors
}

 

 

repository using Spring Data JPA : 

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

    public Employee findByName(String name);

}

 

 

 

이 둘이 persistence layer 임

 

 

Test Class 작성

@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeRepositoryIntegrationTest {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private EmployeeRepository employeeRepository;

    // write test cases here

}

 

@RunWith(SpringRunner.class)는 Spring Boot 테스트 기능과 JUnit 간의 브리지를 제공한다.

JUnit 테스트에서 Spring Boot 테스트 기능을 사용할 때마다 이 주석이 필요하다.

@DataJpaTest는 지속성 계층을 테스트하는 데 필요한 몇 가지 표준 설정을 제공한다.

  • 메모리 내 데이터베이스인 H2 구성
  • Hibernate, Spring Data 및 DataSource 설정
  • @EntityScan 수행
  • SQL 로깅 켜기

DB 작업을 수행하려면 데이터베이스에 이미 일부 레코드가 필요하다. 

이 데이터를 설정하기 위해 TestEntityManager를 사용할 수 있다 .

Spring Boot TestEntityManager는 테스트를 작성할 때 일반적으로 사용되는 메서드를 제공하는 표준 JPA EntityManager를 대신한다 .

EmployeeRepository 는 테스트할 구성 요소로 보면 된다

 

 

첫번째 테스트 케이스 작성

@Test
public void whenFindByName_thenReturnEmployee() {
    // given
    Employee alex = new Employee("alex");
    entityManager.persist(alex);
    entityManager.flush();

    // when
    Employee found = employeeRepository.findByName(alex.getName());

    // then
    assertThat(found.getName())
      .isEqualTo(alex.getName());
}

이렇게 하면 TestEntityManager를 사용하여 DB에 insert하고 find by name API를 확인한다

https://www.baeldung.com/introduction-to-assertj

 

Introduction to AssertJ | Baeldung

AssertJ is an open-source library for writing fluent and rich assertions in Java tests. This article focuses on tools available in the basic AssertJ-core module.

www.baeldung.com

 

 

 

Unit Testing With @WebMvcTest

Controller는 Service layer에 따라 다르다. 단순하게 단일 메소드로 포함된 컨트롤러이다

@RestController
@RequestMapping("/api")
public class EmployeeRestController {

    @Autowired
    private EmployeeService employeeService;

    @GetMapping("/employees")
    public List<Employee> getAllEmployees() {
        return employeeService.getAllEmployees();
    }
}

 

 

controller 에 대한 단위 테스트는 Service 에 @MockBean 을 적용한다

@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeRestController.class)
public class EmployeeRestControllerIntegrationTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private EmployeeService service;

    // write test cases here
}

 

컨트롤러를 테스트하기 위해 @WebMvcTest를 사용할 수 있다

단위 테스트를 위해 Spring MVC 인프라를 자동 구성한다.

대부분의 경우 @ WebMvcTest는 단일 컨트롤러를 부트스트랩하는 것으로 제한되며 또한 @MockBean 과 함께 사용하여 필요한 종속성에 대한 모의 구현을 제공할 수 있다.

@WebMvcTest는 또한 전체 HTTP 서버를 시작하지 않고도 MVC 컨트롤러를 쉽게 테스트할 수 있는 강력한 방법을 제공하는 MockMvc를 자동 구성한다

 

 

@Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
  throws Exception {
    
    Employee alex = new Employee("alex");

    List<Employee> allEmployees = Arrays.asList(alex);

    given(service.getAllEmployees()).willReturn(allEmployees);

    mvc.perform(get("/api/employees")
      .contentType(MediaType.APPLICATION_JSON))
      .andExpect(status().isOk())
      .andExpect(jsonPath("$", hasSize(1)))
      .andExpect(jsonPath("$[0].name", is(alex.getName())));
}

get (…) 메소드 호출은 put() , post() 등과 같은 HTTP 동사에 해당하는 다른 메소드로 대체하면 된다. 

또한 요청에서 콘텐츠 유형을 설정할 수 있다오.

MockMvc 는 유연하며 이를 사용하여 모든 요청을 생성할 수 있다

 

 

 

Auto-Configured Tests

(귀찮아서 통 번역하고 읽어봄ㅡㅜ)

 

Spring Boot의 자동 구성 주석의 놀라운 기능 중 하나는 코드베이스의 전체 애플리케이션 및 테스트별 레이어의 일부를 로드하는 데 도움이 된다는 것이다.

위에서 언급한 주석 외에도 널리 사용되는 몇 가지 주석 목록은 다음과 같다.

  • @WebF luxTest  @WebFluxTest 주석을 사용하여 Spring WebFlux 컨트롤러를 테스트할 수 있다.  필요한 종속성에 대한 모의 구현을 제공하기 위해 종종 @MockBean 과 함께 사용된다 .
  • @JdbcTest : @JdbcTest 주석을 사용하여 JPA 애플리케이션을 테스트할 수 있지만 DataSource만 필요한 테스트용이다 . 주석은 메모리 내장형 데이터베이스와 JdbcTemplate을 구성한다 .
  • @JooqTest : jOOQ 관련 테스트를 테스트하기 위해  DSLContext를 구성하는 @JooqTest 주석을 사용할 수 있다.
  • @DataMongoTest : MongoDB 애플리케이션을 테스트하기 위해 @DataMongoTest 는 유용한 주석이다. 기본적으로 드라이버가 종속성을 통해 사용 가능한 경우 인메모리 임베디드 MongoDB를 구성하고, MongoTemplate을 구성하고, @Document 클래스 를 스캔 하고, Spring Data MongoDB 리포지토리를 구성한다.
  • @DataRedisTest를 사용 하면 Redis 애플리케이션을 더 쉽게 테스트할 수 있다. 기본적으로 @RedisHash 클래스를 스캔 하고 Spring Data Redis 리포지토리를 구성한다.
  • @DataLdapTest는  인메모리 내장 LDAP (사용 가능한 경우)를 구성하고, LdapTemplate을 구성하고, @Entry 클래스 를 스캔 하고, 기본적으로 Spring Data LDAP 리포지토리를 구성한다.
  • @RestClientTest : 우리는 일반적으로 @RestClientTest 주석을 사용하여 REST 클라이언트를 테스트한다. Jackson, GSON 및 Jsonb 지원과 같은 다양한 종속성을 자동 구성한다. RestTemplateBuilder를 구성한다 . 기본적으로 MockRestServiceServer  에 대한 지원을 추가한다 .
  • @JsonTest : JSON 직렬화를 테스트하는 데 필요한 Bean으로만 Spring 애플리케이션 컨텍스트를 초기화한다.

Spring 통합 테스트 최적화 기사에서 이러한 주석과 통합 테스트를 추가로 최적화하는 방법에 대해 자세히 알아볼 수 있다 .

 

Optimizing Spring Integration Tests | Baeldung

An opinionated guide to implementing and optimizing integration tests with Spring.

www.baeldung.com

 

 

728x90