테스트코드(Test Code)

테스트 코드 - 회원 관리 예제 실습(회원 도메인과 리포지토리 생성)

Junuuu 2022. 2. 5. 00:01
728x90

이전에 회원 관리 예제를 작성하는 실습(회원 도메인과 리포지토리 생성)을 진행해 보았습니다.

개발한 기능을 main 메서드를 통해서 실행하거나, 웹 애플리케이션의 컨트롤러를 통해서 해당 기능을 실행하게 되면 준비하고 실행하는데 시간이 오래 걸립니다.

또한 반복적으로 테스트하거나 여러 테스트를 한꺼번에 하기 어렵다는 문제점이 있습니다.

이를 통해 테스트 코드를 작성해보고자 합니다.

 

https://junuuu.tistory.com/75

 

스프링 부트 - 회원 관리 예제 실습

비즈니스 요구사항 정리 데이터 : 회원 ID, 이름 기능 : 회원 등록, 조회 아직 데이터 저장소가 선정되지 않은 가상의 시나리오(어떤 데이터베이스를 사용할지 정해지지 않음) 일반적인 웹 애플리

junuuu.tistory.com

 

1. 자바에는 src/main이 존재하고 src/test가 존재합니다. 이때 src/test에 hello.hellospring.repoisotry 패키지를 생성합니다.

 

2. hello.hellospriong.repository 패키지에 MemoryMemberRepositoryTest 클래스를 생성합니다.

일반적으로 테스트하고자 하는 클래스 이름 뒤에 Test를 붙여 이름 짓습니다.

 

3.  MemoryMemberRepositoryTest.java 파일에 아래와 같이 입력하고 실행해봅니다.

package hello.hellospring.repository;
import org.junit.jupiter.api.Test;

public class MemoryMemberRepositoryTest {
	MemberRepository repository = new MemoryMemberRepository();
	
	@Test
	public void save() {
		
	}
}

Run As -> JUnit Test를 실행하게 되면 아무것도 없기 때문에 당연히 테스트에 오류가 발생하지 않습니다.

 

 

4. MemoryMemberRepositoryTest.java 파일에서 실제로 테스트를 진행합니다.

package hello.hellospring.repository;
import org.junit.jupiter.api.Test;
import hello.hellospring.domain.Member;

public class MemoryMemberRepositoryTest {
	MemberRepository repository = new MemoryMemberRepository();
	
	@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));
	}
}

Member 객체를 생성합니다.

객체에 이름의 "Spring"으로 지정합니다.

객체를 Repository에 저장합니다.

result에 repository로부터 id값을 기반으로 찾은 객체를 저장합니다. (이때 Optional을 꺼내기 위해 get을 사용합니다.)

get을 사용하는 것이 바람직하진 않으나 테스트 코드에서는 이렇게 사용한다고 합니다.

result==member 가 같은 값인지 출력문을 통해 확인합니다.

 

하지만 위의 방식처럼 글자로 일일이 확인하기 힘들기 때문에 Assert를 사용합니다.

출력 구문 대신에 member와 result를 비교해주는 Assertions.assertEquals(member, result); 구문을 입력합니다.

 

출력문은 사라졌지만 녹색불을 통해 테스트가 정상적으로 동작함을 알 수 있습니다.

 

만약 Assertions.assertEquals(member, null); 구문을 입력하고 테스트한다면 어떻게 될까요?

빨갛게 테스트 실패 즉, 에러가 발생합니다.

 

그리고 테스트를 조금 더 편하게 사용하기 위해서 다음과 같이 assertThat을 사용합니다.

package hello.hellospring.repository;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;

import hello.hellospring.domain.Member;


public class MemoryMemberRepositoryTest {
	MemberRepository repository = new MemoryMemberRepository();
	
	@Test
	public void save() {
		Member member = new Member();
		member.setName("Spring");
		repository.save(member);
		Member result = repository.findById(member.getId()).get();
		assertThat(member).isEqualTo(result);				
	}
}

assertThat.isEqualTo의 메서드 이름을 통해 member와 result가 같은 것인지 비교하는구나를 알 수 있습니다.

이제 save() 메서드에 대한 테스트가 완료되었습니다.

 

다음은 findByName() 메서드 테스트입니다.

package hello.hellospring.repository;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;

import hello.hellospring.domain.Member;


public class MemoryMemberRepositoryTest {
	MemberRepository repository = new MemoryMemberRepository();
	
	@Test
	public void save() {
		Member member = new Member();
		member.setName("Spring");
		repository.save(member);
		Member result = repository.findById(member.getId()).get();
		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("Spring2").get();
		assertThat(result).isEqualTo(member1);
	}
	
}

위에서 했던 것과 동일하게

 assertThat(result). isEqualTo(member1); 구문을 실행하면 에러가 발생할 것입니다.

 assertThat(result). isEqualTo(member2); 구문을 실행하면 정상적으로 테스트가 완료됩니다.

 

다음은 findAll() 메서드 테스트입니다.

package hello.hellospring.repository;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;

import java.util.List;
import hello.hellospring.domain.Member;


public class MemoryMemberRepositoryTest {
	MemberRepository repository = new MemoryMemberRepository();
	
	@Test
	public void save() {
		Member member = new Member();
		member.setName("Spring");
		repository.save(member);
		Member result = repository.findById(member.getId()).get();
		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);
	}
}

result에 findAll()을 반환하는 List <Member> 값을 입력받습니다.

이후에 List의 사이즈를 비교해보면 값을 2개 저장했기 때문에 2여야 합니다.

 

테스트를 수행하면 어떻게 될까요?

에러가 발생합니다..

 

findByName()은 테스트가 분명히 잘 되었는데 왜 에러가 발생할까요?

모든 테스트는 순서가 보장되지 않습니다.

따라서 순서에 의존적으로 설계하면 절대 안 됩니다.

findAll()을 테스트할 때 이미 repository에 값을 저장했기 때문에 findByName()을 테스트할때 다른 참조 변숫값을 같은 객체가 나와버린 것입니다.

 

따라서 테스트가 한번 끝나고 나면 데이터를 매번 clear 해야 합니다.

 

따라서 MemoryMemberRepository.java 에 다음과 같은 코드를 추가합니다.

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

 

MemoryMemberRepositoryTest.java에는 코드를 다음과 같이 수정합니다.

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

MemberRepository에서 MeoryMemberRepository로 변경한 이유는 MemberRepository 인터페이스에는 clearStore() 메서드가 구현되어 있지 않습니다.

 

이후에 @AfterEach 어노테이션을 통해 테스트가 실행될 때마다 afterEach() 메서드가 실행되도록 합니다.

3개의 테스트가 무사히 완료되었습니다

 

지금은 main 코드를 만들고 test를 실행하였습니다.

이 방법을 뒤집어서 test코드를 먼저 작성하고 main을 만드는 것이 바로 TDD 개발 기법입니다.