회원 리포지토리 테스트 케이스 작성

Sunny·2022년 11월 16일
0

✨ 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술


🔅 목차


✅회원 관리 예제 - 백엔드 개발

#1 비즈니스 요구사항 정리
#2 회원 도메인과 리포지토리 만들기
#3 회원 리포지토리 테스트 케이스 작성
#4 회원 서비스 개발
#5 회원 서비스 테스트


🔅 회원 리포지토리 테스트 케이스 작성

❗기존의 테스트 방법

개발한 기능을 실행해서 테스트 할 때 다음과 같은 방법을 썼다.

  • 방법1 : 자바의 main 메서드를 통해 실행하여 테스트
  • 방법2 : 웹 어플리케이션의 컨트롤러를 통해서 해당 기능 실행

이러한 방법은 준비하고 실행하는데 오래 걸리고, 반복 실행하기 어려우며, 여러 테스트를 한번에 실행하기 어렵다는 단점이 있다.

🌱 JUnit을 이용한 테스트

자바는 JUnit이라는 프레임워크로 테스트를 실행해서 위와 같은 문제를 해결한다.

다음은 JUnit을 이용한 회원 리포지토리 메모리 구현체 테스트이다.

  1. MemoryMemberRepository를 테스트하기 위한 MemoryMemberRepositoryTest 클래스를 만든다.
  • 경로 : /test/hello/hellospring/repository
  • 클래스명 : MemoryMemberRepositoryTest

  1. 객체를 생성한다.

    MemoryMemberRepository repository = new MemoryMemberRespository();
  2. 하고 싶은 테스트를 진행한다.

🟦 save 기능이 동작하는지 테스트

(1) 테스트를 할 수 있는 상태로 만들어 주기.

@Test를 메서드 위에 붙여주면, 해당 메서드를 테스트 대상으로 지정할 수 있으며, 그 메서드를 실행시킬 수 있는 상태가 된다.
org.junit.jupiter.api 를 import해야 실행할 수 있다는 점 알아두자.

실제로 실행을 시켜보면 아래와 같은 상태가 된다.
save() 메서드가 제대로 동작하는 것을 확인할 수 있다.

(2) 테스트 할 name을 세팅한다.

Member member = new Member();
member.setName("spring");

(3) 테스트 데이터를 저장한다.

repository.save(member);

(4-1) 검증
이제, 내가 넣은 데이터가 제대로 저장이 되는지 검증해보자.

// Optional을 사용하였으므로, get()으로 데이터 꺼내기
Member result = repository.findById().get();

// 세팅한 데이터와 저장한 데이터가 같다고 제대로 출력이 되는지 확인
// 참이면 true 출력
System.out.println("result=" + (result == member));

// 위의 방법처럼 글자로 확인하는 방법 말고 

(4-2) Assertions를 이용한 검증

위의 방법처럼 글자로 확인하는 방법말고 더 좋은 다른 방법이다.

Assertions.assertEquals(member, result);

녹색불이 뜨면 성공, 빨간불이 뜨면 실패다.

비록 출력되는건 없지만, 녹색불이 나온 것으로 보아 테스트가 잘 됐음을 알 수 있다.

만약에 result와 member가 다르다면 어떻게 될까?
result 대신에 null을 넣고 test하면 다음과 같다.

Assertions.assertEquals(member, null);

기대하는 값이 있지만, 실제로는 null이라서 다르다고 결과가 뜬 것이다.

(4-3) assertThat을 이용한 검증

Assertions.assertThat(member).isEqualTo(result);

Assertions는 static import를 할 수 있어서 다음과 같이 해주면 Assertions 을 안써도 바로 assertThat 메서드를 사용할 수 있다.

assertThat(member).isEqualTo(result);

save 기능 테스트 관련하여 전체 자바 코드는 다음과 같다.

class MemoryMemberRepositoryTest {
	
    MemoryMemberRepository repository = new MemoryRepository();
    
    @Test
    public void save() {
    	Member member = new Member();
        member.setName("spring"); 
        
        repository.save(member); 
        
        Member result = repository.findById(member.getId()).get();
               
        System.out.println("result = " + (result == member));
        
        // 위의 코드는 글자로 확인이 불가하므로 Assertion으로 다시 확인해본다
        // member와 result가 똑같은지 실행해볼 수 있다.
        Assertions.assertEquals(member, result);
        // 의도적으로 result 대신 null을 넣어서 실패 결과를 확인
        Assertions.assertEquals(member, null);
        
        // 요즘에는 위의 코드보다 하단의 코드를 많이 사용
        assertThat(member).isEqualTo(result);   	
    }
}

✔cf - Junit의 Assertion 클래스
모든 Assertion은 Assert 클래스에 있다.

public class Assert extends java.lang.Object

이 클래스는 테스트 작성에 유용한 Assertion 메서드 집합을 제공한다. 실패한 Assertion 만 기록된다.

Assert는 프로그램에 대한 가정을 테스트 할수 있는 것으로, 프로그램의 오류를 감지하고 수정하는 효과적인 방법을 제공한다.

🟦 findByName 기능이 동작하는지 테스트

(1)spring1, spring2라는 이름의 회원을 가입시켜 테스트를 진행한다.

@Test
public void findByName() {
	Member member1 = new Member();
    member1.setName("spring1");
    repository.save(member1);
    
    Member member2 = new Member();
    member2.setName("spring2");
    repository.save(member2);
    
    // 여기에 spring2를 넣으면 빨간불이 뜬다
    Member result = repository.findByName("spring1").get();
    
    assertThat(result).isEqualTo(member1);
    
}

아래 코드와 같이 작성하여 result와 member가 같은지 확인해보면 일치하다는 성공의 결과가 뜬다.

Member result = repository.findByName("spring1").get();

🟦 findAll 기능이 동작하는지 테스트

@Test
public void findAll() {
	Member member1 = new Member();
    member1.setName("spring1");
    repository.save(member1);
    
    Member member2 = new Member();
    member2.setName("spring2");
    repository.save(member2);
    
    List<Member> result = repository.findAll();
    
    assertThat(result.size()).isEqualTo(2);
}

위의 코드대로 테스트한 결과 정상적으로 테스트가 되었다.

🌱 클래스 레벨에서 전체 기능 테스트

테스트는 클래스 레벨에서도 테스트가 가능하여 한꺼번에 테스트가 가능하다.

findAll 기능 테스트 코드까지 작성한 상태에서, 클래스 레벨에서 전체 기능 테스트를 하면 에러가 난다. 다 성공했었던 건데 왜 에러가 날까?

테스트는 순서가 보장되지 않는다.
결과를 보면 findAll() 부터 실행이 되었기 때문에 에러가 나는 것이다.

순서에 의존적으로 설계하면 절대 안된다.

테스트가 하나 끝나고 나면, 데이터는 clear 해주어야한다.
테스트가 하나 끝날 때 마다 리포지터리가 지워지는 코드를 작성해야한다.

우선, 기존의 MemoryMemberRepository.java에 다음 코드를 추가한다.
데이터를 clear 해주는 코드이다.

    public void clearStore() {
        store.clear();
    }

다음, 테스트 파일(MemoryMemberRepositoryTest.java)에 다음 코드를 추가한다.

@AfterEach
public void afterEach(){
	repository.clearStore(); // 메서드가 끝날 때마다 저장소를 다 지운다.
}

@AfterEach는 메서드가 끝날 때마다 동작을 하도록 하는 어노테이션이다.
위의 테스트 코드를 예로 들면 다음과 같이 동작한다.

save() > afterEach() > findByName() > afterEach() > findAll() > afterEach()

이제 클래스 레벨 단위로 테스트를 해보면 순서와 상관없이 진행되기 때문에 코드가 다 성공하는 것을 확인할 수 있다.

테스트는 서로 의존 관계 없이 설계가 되어야하기 때문에 하나의 테스트가 끝나면 저장소나 공용데이터를 깔끔하게 지워주어야한다.

🌱 테스크 코드 전체 코드

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.assertj.core.api.Assert;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.*;

class MemoryMemberRepositoryTest {

    //MemberRepository repository = new MemoryMemberRepository();
    MemoryMemberRepository repository = new MemoryMemberRepository();  // MemoryMemberRepository만 테스트하는것이므로 바꿔놓는다

    @AfterEach
    public void afterEach() {
        repository.clearStore(); 
    }

    @Test
    public void save(){
        Member member = new Member();
        member.setName("spring");

        repository.save(member);

        Member result = repository.findById(member.getId()).get();

        // 검증
        System.out.println("result = " + (result == member));

        // 글자로 볼 수 없어서 Assertions라는 기능이 있다.
        // result와 member와 똑같은지 실행해볼수 있다.
        // Assertions.assertEquals(member, result);
        // Assertions.assertEquals(member, null);  // 의도적으로 실패가 뜨도록 하여 결과 확인

        // 요즘에는 이걸 많이 쓴다.
        //Assertions.assertThat(member).isEqualTo(result);
        assertThat(member).isEqualTo(result);
    }

    @Test
    public void findByName(){
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);

        Member result = repository.findByName("spring1").get();

        assertThat(result).isEqualTo(member1);
    }

    @Test
    public void findAll() {
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);

        List<Member> result = repository.findAll();

        assertThat(result.size()).isEqualTo(2);
    }

}

🌱 테스트 주도 개발(TDD, Test-Driven-Development)

테스트 주도 개발(TDD)는 설계 이후 코드 개발 및 테스트 케이스를 작성하는 기존의 개발 프로세스와 다르게 테스트 케이스를 작성한 후 실제 코드를 개발하여 리팩토링하는 절차를 따른다.

profile
개발에 재미를 붙여보기 :)

0개의 댓글