[Spring] Unit Test(단위 테스트) 작성의 필요성
반응형

테스트의 종류

Unit Test(단위 테스트)

하나의 모듈을 기준으로 독립적으로 진행되는 가장 작은 단위의 테스트이다. 즉 하나의 기능 또는 메소드에 대한 독립적인 테스트가 1개의 단위테스트가 될 수 있다.

테스트 대상 단위의 크기를 작게 설정하여 단위 테스트를 최대한 간단하고 디버깅하기 쉽게 작성해야한다.

소프트웨어를 개발할 때, 소프트웨어 내부 구조나 구현 방법을 고려하여 개발자 관점에서 테스트한다. 그러므로 소프트웨어 내부 코드에 관련한 지식을 반드시 알고 있어야 하는 화이트박스 테스트이다.

 

Integration Test(통합 테스트)

단위 테스트보다 더 큰 동작을 달성하기 위해 여러 모듈들을 모아 이들이 의도대로 협력하는지 확인하는 테스트이다.

통합테스트는 단위 테스트와 달리 개발자가 변경할 수 없는 부분까지 묶어 검증할 때 사용한다.

한편, 통합 테스트의 단점은 단위 테스트보다 더 많은 코드를 테스트하기 때문에 신뢰성이 떨어질 수 있다는 점이다. 또,어디서 에러가 발생했는지 확인하기 쉽지 않아 유지보수하기 힘들다는 점도 있다.

 

Acceptance Test(인수 테스트)

인수 테스트는 사용자 스토리(시나리오)에 맞춰 수행하는 테스트이다.

소프트웨어를 인수할 때, 소프트웨어 내부 구조나 구현 방법을 고려하기보다는 실제 사용자 관점에서 테스트하는 경우가 많다. 따라서, 인수 테스트는 소프트웨어 내부 코드에 관심을 가지지 않는 블랙박스 테스트이다. 실제 사용자 관점에서 테스트할 때 주로 E2E(End-to-End) 형식을 이용해서 확인한다.

인수 테스트는 주로 API를 확인하는 방식으로 이뤄진다.

 

 

단위 테스트 작성의 필요성

일반적으로 실무에서 테스트 코드를 작성한다고 하면 거의 단위테스트를 의미한다. 테스팅에 대한 시간과 비용을 절감할 수 있으며 새로운 기능 추가 시 수시로 빠르게 테스트가 가능하다. 그렇기 때문에 단위 테스트를 선호하며, 요즘 많이 사용되는 TDD(Test-Driven Development)에서 말하는 테스트도 단위 테스트를 의미한다.

TDD(Test-Driven Development)
테스트케이스를 미리 작성 한 후 테스트를 통과하도록 코드를 개발하여 리팩토링하는 절차를 말한다.
애자일 방법론의 XP의 원리 중 하나이다..

 

좋은 단위 테스트 특징

좋은 테스트 코드는 FIRST라는 5가지 규칙을 따른다.

1. Fast : 테스트는 빠르게 동작하고 자주자주 돌릴 수 있어야 한다.

2. Independent : 각각의 테스트는 독립적이어야 한다. 의존해서는 안된다.

3. Repeatable : 어느 환경에서도 반복 가능해야 한다.

4. Self-Validating : 테스트는 성공 또는 실패로 결과를 내어야 한다.

5. Timely : 테스트하려는 실제 코드를 구현하기 직전에 구현해야 한다.

단위 테스트는 생각보다 개발에 있어 중요한 것 같다. 위 규칙들을 기억해두자.. CleanCode책에 나오는 내용이다.

 

 

개발자 테스트 코드 작성 장단점

개발자 본인이 작성한 코드는 본인이 가장 잘 안다.
-> 개발자가 직접 본인이 작성한 코드를 검증하기위해 테스트 코드 작성

장점

  • 빠르고 정확한 테스트가 가능하다. (예상한 동작 vs 실제 동작)
  • 테스트 자동화 가능 -> 배포 절차 시 테스트 코드가 수행되어 동작 검증
  • 리팩토링 후 기존 동작에 대한 검증을 쉽게 할 수 있다.

 

단점

  • 개발 시간이 오래 걸린다. -> 테스트 코드를 작성하는데 시간이 꽤 걸린다..
  • 테스트 코드를 유지보수해야한다.
    -> 기능이나 동작을 수정하게 되면 테스트 코드도 그에 맞게 유지보수해야 한다.

 

 

JUnit5를 이용한 단위 테스트 코드 작성예제

Spring에서의 단위테스트는 JUnit과 AssertJ라는 라이브러리가 필요한데 만약 spring boot로 프로젝트를 구성하였다면 따로 설정해주지 않아도 포함되어 있다.

 

given/when/then 패턴

given : 어떠한 데이터가 준비되었을 때, 즉 주어졌을 때

when : 어떤 함수를 실행하면

then : 어떤 결과가 나와야 한다.

위 패턴을 기억하고 예제를 보자.

class UserTest {
    @Nested
    @DisplayName("회원가입 요청, User 객체 생성")
    class CreateUserInfo {
        private String username;
        private String password;
        private String email;

        @BeforeEach
        void setup() {
            username = "user1";
            password = "password1234";
            email = "xxxx@naver.com";
        }

        @Test
        @DisplayName("정상 케이스")
        void createUserInfo_Normal() {
            //when
            User user = new User(username, password, email);

            //then
            assertEquals(user.getUsername(), username);
            assertEquals(user.getPassword(), password);
            assertEquals(user.getEmail(), email);
        }
}

자세한 코드까지 다 보여주기엔.. 너무 양이 많으니 딱 테스트하는 부분만 가져왔다. 위 테스트를 간단하게 보자면 setup()을 통해 회원에 대한 데이터가 주어졌을 때(given) User를 생성하면(when) 생성한 User 객체 값들과 given으로 주어졌던 값이 같아야 한다.(then)

테스트를 수행하면..

이런 결과가 나올 것이다.. 정상적으로 작동하는지 정상 케이스를 넣어 테스트 해보았다. 위에서 사용한 어노테이션에 대해 간단하게 알아보자.

@Nested : 계층 구조의 테스트 코드를 작성할 수 있도록 해준다. 이 어노테이션을 사용하면 위 결과 사진에서 보이듯이 회원가입 요청, User객체 생성 부분에 해당하는 것들만 묶어 계층구조로 만들었다.

@DisplayName("...") : 결과에 보일 테스트 이름을 지정할 수 있다.

@BeforeEach : 각 테스트 함수가 불리기 전에 매번 호출 된다.

@BeforeAll : 테스트 함수가 불리기 전에 딱 한 번 호출 된다.

@Test : 테스트할 하나의 단위, 즉 테스트 1개에 해당한다.

이 외에도 다양한 어노테이션이 있으니 필요할 때 찾아보자..
반응형