개발한 기능을 테스트하기 위해서 자바의 main 메서드를 통해 실행하거나, 웹 어플리케이션의 컨트롤러를 통해서 해당 기능을 실행한다. 하지만 이러한 방법을 사용하기까지 시간 소모도 크고, 반복 실행도 어려우며, 여러 테스트를 한번에 실행하기가 어렵다.
이에 자바는 JUnit이라는 프레임워크로 테스트를 진행해서 이러한 문제를 해결한다.
테스트 케이스 작성하기
srt/test/java의 하위 디렉터리에 [repository]라는 Package를 생성한 후, 테스트할 레포지토리 명 뒤에 'Test'를 붙인 [MemoryMemberRepositoryTest]라는 Java Class를 생성한다. 다른 곳에서 끌어서 사용할 것이 아니기 때문에 public 접근 제어자를 지워도 상관 없다.
먼저 class 내에 repository 하나를 선언한다.
MemberRepository repository = new MemoryMemberRepository();
그리고 @test 어노테이션을 선언하면 JUnit 프레임워크가 import된다. 그러면 아무런 로직이 없는 save() 함수를 작성하고 빌드해보면 실행이 잘 되는 것을 알 수 있다.
package springTest.hellospring.repository;
import org.junit.jupiter.api.Test;
class MemoryMemberRepositoryTest {
MemberRepository repository = new MemoryMemberRepository();
@Test
public void save() {
}
}
이제부터 MemoryMemberRepository에 구현한 함수를 하나씩 호출하고 구현하면서 오류가 발생하는지 확인할 것이다.
먼저 save 함수의 로직을 구현한다. 저장할 새로운 Member 객체의 member를 선언한 후, 'younngjun'이라는 name 프로퍼티를 대입하였다. 이후 repository에 저장한 다음, findById 메서드를 호출하여 저장한 member를 result 변수에 저장한다.
물론 println 구문을 통해 확인할 수 있지만 Assertions의 assertThat을 통해 member와 result가 일치하는지 확인할 수도 있다.
실무에서는 Build Tool이랑 엮어서 Build Tool에서 빌드할 때 Test Case를 통과하지 않으면 다음 단계로 넘어가지 못하도록 막는 방법을 사용한다고 한다.
package springTest.hellospring.repository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import springTest.hellospring.domain.Member;
class MemoryMemberRepositoryTest {
MemberRepository repository = new MemoryMemberRepository();
@Test
public void save() {
Member member = new Member();
member.setName("younngjun");
repository.save(member);
// Optional에서 값을 꺼낼 때는 .get()으로 꺼낼 수 있지만 좋은 방법이 아니다.
Member result = repository.findById(member.getId()).get();
// System.out.println("result = " + (result == member));
Assertions.assertThat(member).isEqualTo(result);
}
}
특정 메서드의 빌드는 메서드의 좌측 ▷ 버튼을 눌러 실행할 수 있다. 에러가 없다면 콘솔에서 빌드가 잘 진행된다.
다음은 findByName 메서드이다. name이 'spring1'인 member1과 'spring'인 member2를 생성한 후 repository에 저장한다.
먼저 'spring1'인 Member를 추출한 후 result에 저장하고, Assertions의 assertThat 구문을 통해 member1과 동일한지 확인한다.
@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();
Assertions.assertThat(result).isEqualTo(member1);
}
JUnit 테스트의 장점은 여러 클래스를 동시에 실행할 수 있다는 것이다. 다음과 같이 Class 단위로 테스트할 수도 있고, 패키지의 모든 Class를 테스트할 수도 있다.
다음은 findAll 메서드이다. 마찬가지로 name이 'spring1'인 member1과 'spring'인 member2를 생성한 후 repository에 저장하였다. 이후 assertThat을 통해 전체 Member 리스트인 result의 길이가 2인지 확인해보았다.
@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);
}
이때 전체 Class를 빌드해보면 에러가 발생한다. 그것도 findByName 메서드에서 말이다. 왜 그럴까?
좌측 하단의 메서드 실행 순서를 확인해보면 findAll 메서드가 먼저 실행된 것을 확인할 수 있다. 즉, 전체 클래스를 실행했을 때 메서드의 실행 순서는 보장이 되지 않는다.
즉 findAll에서 이미 'spring1'인 member1과 'spring'인 member2를 생성하였기 때문에, findByName 메서드에서 다시 똑같은 name을 대입하려다 보니 에러가 발생하는 것이다.
어떻게 해야 할까?
바로 테스트가 하나 끝날 때 마다 데이터를 Clear 해주는 코드를 실행하면 된다!!
바로 @AfterEach를 통해서다. @AfterEach를 하면 어떤 메서드가 실행이 끝날 때 마다 어떤 동작을 하게 하는 콜백 메서드이다.
@AfterEach
public void afterEach() {
}
우선 MemoryMemberRepository만 테스트하는 것이기 때문에 repository를 MemberRepository 인터페이스가 아닌 MemoryMemberRepository로 변경한다.
이후 MemoryMemberRepository로 이동하여 clearStore() 메서드를 구현한다.
public void clearStore() {
store.clear();
}
다음 MemoryMemberRepositoryTest로 이동하여 구현한 메서드를 호출한다.
@AfterEach
public void afterEach() {
repository.clearStore();
}
이제 Class 단위로 테스트를 진행해도 메서드가 끝날 때마다 @AfterEach 때문에 store인 repository가 clear() 되기 때문에 모든 메서드가 정상적으로 동작하는 것을 확인할 수 있다.
지금까지는 MemoryMemberRepository의 메서드를 구현하고 Test를 작성하였지만, 거꾸로 Test를 작성하고 MemoryMemberRepository의 메서드를 구현하기도 한다. 이러한 방식을 테스트 주도 개발, TDD라고 한다. 테스트를 만들고, 구현 클래스를 만들어서 돌려보는 것이다.
출처
'🌱 Spring > 회원 관리 예제' 카테고리의 다른 글
[Spring] 웹 UI 및 비즈니스 로직(가입, 조회) 연결 (0) | 2023.07.05 |
---|---|
[Spring] Spring Bean과 의존 관계 (0) | 2023.07.05 |
[Spring] 회원 서비스 테스트(Dependency Injection) (0) | 2023.07.05 |
[Spring] 회원 Service 개발 (0) | 2023.07.03 |
[Spring] 회원 Domain과 Repository 구현 (0) | 2023.07.02 |