TIL - JAVA spring DAY 5

jihan kong·2022년 1월 8일
0

JAVA spring 입문

목록 보기
6/20
post-thumbnail

Test case

DAY 4까지 간단한 회원관리 웹 프로그램을 만들기 위해 회원 도메인리포지토리(저장소) 를 생성했다. 그리고 이제 기능을 실제로 Run하면서 어떻게 작동하는지를 체크해야할 단계에 도달했다.

그렇다면, 자바의 main 메서드를 통해서 실행하거나, 웹 어플리케이션의 컨트롤을 통해서 기능을 실행할것이다. but, 우리가 만든 기능은 한 두가지가 아니며, 수정사항을 반영하기 위해 반복 실행해야하고, 무엇보다 아직 완성 단계가 아닌 프로젝트 단계에서 먼저 실행해보고 싶다.

이 때, main 메서드로 매번 실행하기는 참 어려운 일이다. 지금은 쉬운 예제를 통해 간단하게 살펴보았지만 만약, 코드의 볼륨이 몇 천줄에서 몇 만줄에 육박한다면? 사실상 불가능에 가깝다. 따라서 개발자들은 테스트 케이스라는 것을 항상 작성한다.

Test 결과의 Pass/ Nonpass 여부에 따라 움직이며 조금 더 빠르고 디테일하게 이슈에 접근해서 문제를 해결할 수 있게 되고 효율적으로 코딩을 할 수 있게 해준다. 강사님의 말씀으로는 요즘 실무에선 빌드 툴에서 빌드할때, 테스트 케이스에서 통과하지 못하면 아예 다음 단계로 넘어가지 못한다고 하셨다. (개발자가 야근이 많은 이유?)

자바는 JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결하게 된다. 강의에 나온대로 회원관리 테스트 코드를 직접 작성해보면서 학습하였다. 다음의 테스트 코드를 톺아보자.

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
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 {

    MemoryMemberRepository repository = new 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();
	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);

    }
}

Annotation으로 작성된 @Test 하단의 코드들이 회원과 리포지토리를 Test 하기 위한 코드들이다.

save class

  • member 객체를 생성
  • member의 이름은 "spring", 그렇다면 ID는?
    ( ID는 MemoryMemberRepository class의 save class의 멤버변수로 member.setID(++sequence)를 통해 series하게 자동생성하도록 했음. DAY4 참조 )
  • repositorymember 변수 저장
  • Member result = repository.findById(member.getId()).get();
    ( 설계한 대로 ID를 가지고와서 저장하는지 findBy를 통해 검증한다. Optional로 선언된 경우, 위와 같이 get을 통해 값을 가져올수 있음)
  • assertThat(member).isEqualTo(result);
    ( 생성한 객체에서 만들어진 데이터와 DB 즉, repository에서 가져온 데이터가 같은지를 확인하여 검증. assertThat은 Assertions.assertThat(member).isEqualTo(result);을 static으로 import하여 선언한 것)

findByName

  • member1, member2 객체를 생성하고 이름은 각각 "spring1", "spring2"
  • 모두 repository에 저장
  • Member result = repository.findByName("spring1").get();
    ( findByName이 잘 동작하는지 검증을 위해 먼저, repository에 저장한 "spring1"get을 통해 가져옴 )
  • assertThat(result).isEqualTo(member1);
    ( assertion 문법을 이용해 "spring1"을 저장한 resultmember1을 비교해서 True 값 확인 )

findAll

  • 위 클래스와 동일한 방식
  • 다만, result 값을 저장할때 List로 저장하고, spring1spring2가 잘 들어가있는지, list size의 크기로 검증
    ( assertThat(result.size()).isEqualTo(2) )

위의 TestCase가 완성되고 모두 전체 Run을 하게 되면,
다음과 같이, findByName() 에서 case가 통과되지 않는 것을 볼 수 있다. 코드상으로는 문제가 없어보이는데 왜 그럴까?

강사님께서 설명해주시길, 모든 테스트는 순서에 상관없이 즉, 순서에 dependency 하지 않게 동작하게끔 만들어져야한다고 하셨다. 그러나 위의 상황에선 findAll() 을 동작시킬때 spring1, spring2가 메모리에 저장되고 다음 findByName()findAll() 에서 저장된 메모리를 참조하면서 문제가 발생한 것이다.

쉽게말하면, findByName() 클래스가 findAll() 클래스에 영향을 받아 테스트가 통과되지 않고 있었던 것이다. 이러한 의존성 이슈를 해결하기 위해, 다음과 같은 코드를 작성해야함을 알게 되었다.

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

이 클래스의 용도는 테스트가 끝날때마다 공용데이터 즉, repository에 저장된 내용을 clear해주는 역할이라고 보면 된다. 그렇게 된다면 순서와 상관없이 독립적으로 실행이 됨을 보장할 수 있게 되는 것이다. 그리고 테스트를 실행해본다면, 다음과 같이 모두 pass하는 것을 확인할 수 있다.

마무리하며...
이처럼 테스트 코드의 역할의 중요성과 필수성을 학습하였다. 위 예제에서는 개발을 먼저 끝낸 후, 테스트 코드를 작성하는 방식을 취했다. 그러나 테스트 코드를 먼저 작성하고, 그 후에 개발을 진행하는 '테스트 주도 개발' 이른바, TDD방식( Test Driven Development )으로 개발을 진행할 수도 있다는 것을 강사님께서 알려주셨다.

각 방식의 장단점이 분명히 존재할것이고 속한 그룹에서 현재 프로세스에 적합한 개발 방식이 있을것이다. 앞으로 내가 프로젝트에 참여할때, 이러한 부분들을 co-worker들과 협의하고 의논하면서 조금 더 좋은 방향을 제시할 수 있는 프로그래머가 될 수 있기를 희망한다.

profile
학습하며 도전하는 것을 즐기는 개발자

0개의 댓글