본문 바로가기
[inflearn] 스프링 핵심 원리 - 기본편/섹션 2 - 예제 만들기

8. 회원 도메인 테스트와 고려할 점

by 슬픈 야옹이 2023. 9. 27.

지난 포스트에서 개발한 회원 도메인을 테스트해보고, 객체 지향 원칙이 잘 지켜졌는지 점검한다.

https://debuggingworld.tistory.com/85

 

7. 회원 도메인 개발

지난 포스트의 설계를 바탕으로 실제 회원 도메인을 개발해본다. 설계가 잘 되었다면, 설계한대로 코드만 잘 작성해도 동작에 문제가 없어야 한다. https://debuggingworld.tistory.com/83 5. 비즈니스 요구

debuggingworld.tistory.com

 

 

자바 main 메서드를 이용한 방법과 테스트 프레임워크(JUnit)을 이용한 방법 두 가지를 사용해본다.

 

1. 자바 main 메서드를 이용한 방법 (권장하지 않음)

자바 main 메서드를 이용해 직접 print를 찍어보면서 테스트하는 방법이다.

작성이 간단하고 따로 배워야 할 것이 없다는 장점이 있지만, 그만큼 할 수 있는 것이 제한적인 방법이다.

 

core 패키지 하위에 MemberApp 클래스를 생성한다.

memberApp.java

 

클래스에 main 함수를 선언하고 테스트 코드를 작성한다.

MemberApp.java

 

MemberApp.java

package hdxian.hdxianspringcore;

import hdxian.hdxianspringcore.member.Grade;
import hdxian.hdxianspringcore.member.Member;
import hdxian.hdxianspringcore.member.MemberService;
import hdxian.hdxianspringcore.member.MemberServiceImpl;

public class MemberApp {

    public static void main(String[] args) {
        MemberService memberService = new MemberServiceImpl();
        Member member = new Member(1L, "memberA", Grade.VIP);

        memberService.join(member);

        Member findMember = memberService.findMember(1L);
        System.out.println("new member: " + member.getName());
        System.out.println("findMember = " + findMember.getName());

    }


}

 

MemberServiceImpl 인스턴스를 참조하는 MemberService 참조변수를 선언한다.

 

새로운 Member를 생성하고 가입시킨 뒤, member가 정상적으로 조회되는지 확인한다.

새로 생성한 Member 객체의 getName()과 조회한 결과의 getName()이 같으면 테스트 성공이다.

 

실행 결과가 다음과 같이 나오면 성공이다. 안 나오면 아쉽게 된 거고

테스트 결과 (성공)

 

 

 

2. JUint 테스트 프레임워크를 사용하는 방법

좌측의 프로젝트 구성을 보면, main 폴더와 함께 test 폴더가 있는 것을 확인할 수 있다.

test 폴더

 

test 폴더는 프로젝트 생성 시 자동으로 생성되며, 테스트 코드를 위한 폴더로 빌드 및 배포에는 포함되지 않는다.

 

test 경로의 core 패키지 하위에 member 패키지를 생성하고, MemberServiceTest 클래스를 생성한다.

member 패키지

 

MemberServiceTest 클래스에서 다음과 같이 테스트 코드를 작성한다.

MemberServiceTest.java

 

MemberServiceTest.java

package hdxian.hdxianspringcore.member;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

public class MemberServiceTest {

    MemberService memberService = new MemberServiceImpl();

    @Test
    void join() {
        // given
        Member member = new Member(1L, "memberA", Grade.VIP);

        // when
        memberService.join(member);
        Member findMember = memberService.findMember(1L);

        // then
        Assertions.assertThat(member).isEqualTo(findMember);

    }
}

테스트에 필요한 MemberService를 선언한 뒤, 가입 기능을 테스트할 join() 메서드를 선언한다.

 

테스트 코드를 작성하는 방식은 자유지만, 강의에서는 given, when, then의 순서로 테스트 코드를 작성하는 것을 추천하였다.

  • given: 주어진 환경, 상황
  • when: 테스트할 동작(기능)
  • then: 결과

 

즉, 어떠한 환경(given)에서 어떠한 동작을 할 때(when) 어떠한 결과가 나온다(then) 의 순서로 테스트 코드를 작성하는 것이다.

 

join() 메서드의 경우를 보면,

  • given: id가 1, 이름이 memberA, 등급이 VIP인 새로운 member가 있을 때
  • when: 이 member를 가입시킨 뒤 조회해보면
  • then: 조회 결과로 나온 findmember가 실제 가입한 member와 같아야 한다.

 

then 구문에서 Assertions.assertThat() 메서드를 사용했는데, 테스트 코드에서 자주 사용되는 라이브러리다.

assertThat()에 인자로 전달한 값에 대해서 여러가지 검증을 시켜볼 수 있다.

junit이 아닌 assertj의 라이브러리를 import했음에 주의하자. 문법이 약간 다르다.

 

 

작성한 메서드를 실행시키면 테스트가 진행된다.

테스트 코드 실행

 

 

성공 시 테스트 결과는 다음과 같이 나온다.

테스트 결과 (성공 시)

 

실패 시 결과는 다음과 같이 나온다. (테스트 코드의 findMember()의 인자를 1L에서 2L로 변경했다.)

테스트 결과 (실패 시)

 

이전 강의 (스프링 입문)와 마찬가지로, 강의는 테스트 코드의 중요성을 다시 한번 강조한다.

프로젝트 규모가 커짐에 따라 테스트 코드는 사실상 필수가 되었고,

이에 따라 테스트 코드를 잘 설계하고 작성하는 것이 개발자의 중요한 능력 중 하나가 되었기 때문이다.

 

 

 

생각해볼 점

지금까지 설계하고 작성한 코드에 대해 고려해야 할 점이 있다.

정말 객체지향의 원칙이 잘 지켜졌는가에 대한 것이다.

 

MemberServiceImpl의 코드를 보면, 클래스가 구현체까지 의존하고 있다. (new MemoryMemberRepository)

 

DIP 원칙에 따르면, MemberServiceImpl은 MemoryMemberRepository의 존재를 몰라야 한다.

 

다르게 말하면, memberRepository가 참조하는 구현체가 달라져도 MemberServiceImpl는 영향받지 않아야 한다.

 

하지만 지금의 코드로는 그 구현체가 달라지면 MemberServiceImpl의 코드 또한 변경된다.

 

즉 지금의 코드는 DIP 원칙에 위배되는 상태로, 좋지 못한 객체 지향 프로그램이라고 할 수 있다.

 

이에 대한 해결법은 나중에 알아볼 것이고, 결국은 spring이 이러한 문제들의 해결법을 제공할 것이다.

 

이 예제의 목적은 순수 자바 코드로만 객체 지향 프로그래밍을 해보면서 나타나는 문제점을 인식하고, 궁극적으로 spring을 사용하는 이유에 대해 이해하는 것이기 때문이다.