Skills/Spring

Spring - 테스트 코드(TDD) 작성

aoaa 2022. 8. 2. 22:16

 어떤 소프트웨어를 개발한다고 할 때, 소프트웨어의 기능에서 무언가 필요하다거나 잘못된 점이 무엇인지 찾아내는 것부터 시작할 것입니다. 이어서 문제점을 찾은 개발자는 그것을 해결하는 기능을 구현하고, 구현의 검증을 위한 테스트를 수행하게됩니다. 

 이 때, 고전적인 개발 방식에서는 검증을 위해 console 화면에 값을 찍어 기능이 동작하는지 확인해볼 것입니다. 눈에 보이는 확실한 방법이지만 이러한 방식은 개발자의 두뇌에 의존해야 하는 맹점(?)이 있습니다. 사람이 판단하고, 사람에게 의존하는지라 실수가 언젠가는 하나씩 발견될 수도 있을 것입니다. 또한 작업 분량이 많아질 수록 로그를 찾아내기 힘들고, 귀찮아져 간소화하는 일이 생길 수도 있게되는 것이죠. 그래서 구안된 방식이 테스트 주도 개발(Test-Driven development)입니다. 개발자들은 이 테스트 코드 작성을 통해 잘 동작하는 깔끔한 코드를 구현하고자 하는 최종 목적을 달성하고자 합니다. 여기서 '깔끔하다'의 의미는 프로그램 유지보수의 편의, 가독성 등 여러 의미를 나타내고 있습니다. 


1. Springboot에서 Test

 Spring에서는 애플리케이션 테스트를 위해 여러 기능을 제공하는데 크게는 두 가지 모듈을 제공합니다.

첫 번째로 spring-boot-test로 대부분의 핵심 기능을 포함하고 있습니다. 둘 째는 spring-boot-test-autoconfigure로 테스트를 위한 Autoconfiguration를 제공합니다. 

 Spring 프로젝트를 만들 때 "spring-boot-starter-test"를 추가하면 대부분 필요한 Defendency(의존)을 자동으로 설정해줍니다.

 

 Test에는 크게 Unit Test와 Integration Test가 있는데,

Unit Test는 단위 테스트를 의미하며 하나의 모듈을 기준으로 독립적으로 진행되는 가장 작은 테스트의 단위를 뜻하고,

Integration Test는 모듈을 통합하는 과정에서 모듈 간 호환성을 확인하기 위해 수행되는 테스트입니다. 여기서 애플리케이션에서 작동하는 하나의 기능 or 메서드라고 이해할 수 있겠습니다. 

 


2. Unit Test

 Spring 환경에서 Unit Test 하나를 예시로 작성해보겠습니다.

// MemberReposiotory.java
public interface MemberRepository {
    Member findByName(String name);
    void add(Member member)
}    

// MemberSerivice
public interface MemberService {

    void join(Member member); // 회원 가입

    Member findMember(Long memberId); // 회원 조회
}

 생성자 주입을 이용해 의존관계를 주입했습니다. 

import hello.core.AppConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.assertj.core.api.Assertions;


public class MemberserviceTest {

    private Memberservice memberservice;
    private MemberRepository memberrepository;
    
    @BeforeEach
    public void beforeEach() {
    ...
    }
    
    @Test
    void join(){
        // given
        Member member = new Member(1l, "memberA", Grade.VIP);

        // when
        memberService.join(member);
        Member findMember = memberService.findMember(1L);

        // then
        Assertions.assertThat(member).isEqualTo(findMember);
    }

 Test를 진행해보면 성공하는 것을 확인할 수 있습니다. 코드를 보게되면 @SpringBootTest 애노테이션을 사용하지 않았기 때문에 어떠한 Spring Defendency가 추가되지 않은 상태입니다. 

 단위 테스트도 중요하지만, Spring에 많은 의존을 하고 있게 되면, Spring 기반 프로젝트 진행 중에는 부득이하게 통합 테스트를 진행하게 될 것입니다. 

 


3. Integration Test

 @SpringBootTest 애노테이션을 사용하면 필요한 Bean을 모두 올려 Test를 할 수 있게 되는데 클래스를 지정하지 않게되면 전체 애플리케이션을 로드하고 있는 Bean을 생성합니다. 

@ExtendWith(Extension.class) // Junit5 기준
@SpringBootTest
public class IntegrationTest { 
	
    @Autowired
    private MemberService memberservice;
    
    @Test
    public void Member_check(){
    	
        Member member = MemberService.findByName("Voyager003");
        Assertions.assertThat(member).isEqualTo(findMember);
	}
}

 @SpringBootTest 애노테이션 선언을 통해 통합테스트를 작성한 코드입니다. 유닛 테스트와 달리 생성자 주입을 해줄 필요 없이 Spring에서 알아서 주입해주게 됩니다. 

 실무에서는 회사, 조직마다 다르지만 Spring Boot 환경에서는 통합 테스트 50%, 단위 테스트 50% 정도로 구성하는 비율도 있고 정답은 없는 방식이라 상황에 맞게 결정해야 되야하는 사항입니다. 

 

 

 

 

참조

 

 

 

 

 

'Skills > Spring' 카테고리의 다른 글

Spring - BeanFactory & BeanDefinition  (0) 2022.09.01
Spring - DI & Spring Container  (0) 2022.08.31
Spring - Spring Web Layer  (0) 2022.07.27
Spring - MockMvc를 이용한 HTTP 메시지 검증  (0) 2022.07.24
Spring - @RunWIth, @ExtendWith  (0) 2022.07.22