쌓고 쌓다

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

프로그래밍/spring

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

승민아 2023. 5. 1. 14:26

개발한 기능을 테스트할 때 main 메소드를 통해 실행하거나 웹 애플리케이션의 컨트롤러로 기능을 실행한다.

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

그래서 자바의 JUnit이라는 프레임워크로 테스트를 실행한다.

 

src/test/java 하위 폴더에 똑같은 패키지를 생성.

테스트는 관례상 뒤에 테스트할 메소드의 이름과   Test를 붙임

 

save 메소드 테스트

public class MemoryMemberRepositoryTest {
    MemberRepository repository = new MemoryMemberRepository();

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

		// 반환값 optinal이라 get으로 꺼냄
        Member result = repository.findById(member.getId()).get(); 
        
        
        System.out.println("result = " + (member == result));

        //계속 글자를 출력하여 볼 순 없다. -> Assertions 사용
        Assertions.assertEquals(member, result);
        
    }

}

 

 

Assertions   org.assertj.core.api를 요즘은 사용한다.

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

Alt+Enter를 통해 static import가 가능하다.
static import후 바로 assertThat 사용 가능

 

인터페이스 객체 생성?

MemberRepository repository = new MemoryMemberRepository();

MemberRepository는 인터페이스이다.

위와 같이 인터페이스는 객체 생성은 불가능하지만 레퍼런스는 만들 수 있다.

여기에 구현 객체를 생성하여 지정할 수 있다.

구현 객체를 생성하여 지정하지 않고 익명 구현 객체를 사용할 수도 있다.

(참조: https://hyuntaekhong.github.io/blog/java-basic20/)

 

findByName 메소드 테스트

public class MemoryMemberRepositoryTest {
    MemberRepository repository = new MemoryMemberRepository();
    
    @Test
    public void findByName() {
        Member member1 = new Member();
        member1.setName("name1");
        
        Member member2 = new Member();
        member2.setName("name2");
        
        repository.save(member1);
        repository.save(member2);
        
        Member result = repository.findByName("name1").get();
        assertThat(result).isEqualTo(member1);
    }
}

 

각각의 테스트는 독립적

클래스 레벨에서 테스트를 돌리면 테스트 메소들을 모두 테스트한다.

모두 동시에 테스트를 돌렸더니 각각 잘 돌아가던 메소드가 실패가 발생한다.

각 메소드에서 생성하여 리포지토리에 넣은 객체들이 그대로 남아있기에

멤버를 생성하고 테스트하는데 문제가 발생한 것이다.

그래서 각각의 테스트는 독립적으로 실행되게 할 필요가 있다.

=> 테스트 메소드 실행 순서는 랜덤. 순서에 의존적으로 만들지 말자.

 

MemoryMemberRepository.java

public class MemoryMemberRepository implements MemberRepository{

    private static Map<Long, Member> store = new HashMap<>();

    ...
    
    // 추가된 clearStore 메소드
    public void clearStore() {
        store.clear();
    }
    
    ...
}

MemoryMemberRespository 클래스에 Map을 비워주는 메소드를 하나 만들자.

 

이제 테스트 코드에 @AfterEach를 사용하여 메모리DB를 비워주는 작업을 하는 메소드를 작성한다.

@AfterEach는 각 테스트가 종료될때 마다 이 메소드를 실행한다.

 

앞서 인터페이스에 바로 객체를 생성하여 지정하였는데.

이러면 인터페이스에 해당 메소드가 존재하지 않아 사용이 불가능하므로 아래처럼

객체의 레퍼런스 또한 인터페이스가 아닌 클래스로 수정을 해야 함.

// 수정전 : MemberRepository repository = new MemoryMemberRepository();
MemoryMemberRepository repository = new MemoryMemberRepository();

 

findAll 메소드 테스트

public class MemoryMemberRepositoryTest {
    MemoryMemberRepository repository = new MemoryMemberRepository();

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

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

        Member member2 = new Member();
        member2.setName("name2");

        repository.save(member1);
        repository.save(member2);

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

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

 

MemoryMemberRepository를 개발하고 테스트를 작성하는 방식이 아닌 뒤집어서

테스트 클래스를 먼저 작성하고 MemoryMemberRepository를 만들 수도 있다.

-> 테스트 주도 개발이라고 TDD 방식임.

Comments