1.컨트롤러, 서비스, 리포지토리의 역할
컨트롤러: 웹 MVC의 컨트롤러 역할
서비스: 핵심 비즈니스 로직 구현
리포지토리: 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
도메인: 비즈니스 도메인 객체, 예) 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리됨
*아직 데이터 저장소 선정X --> 우선 인터페이스로 구현 클래스를 변경할 수 있도록 설계
개발을 진행하기 위해서 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소 사용
2.TDD란? 왜 하는가?
TDD : 테스트 주도 개발(test-driven development), 선 테스트 후 개발 방식의 프로그래밍 방법.
매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 프로세스 중 하나이다. 개발자는 먼저 요구사항을 검증하는 자동화된 테스트 케이스를 작성한다. 그런 후에, 그 테스트 케이스를 통과하기 위한 최소한의 코드를 생성한다. 마지막으로 작성한 코드를 표준에 맞도록 리팩토링한다.
ex) 테스트 클래스 만든 다음에 틀에 맞게 main 클래스 생성
JUnit이라는 프레임워크로 테스트를 실행
Junit
- "@Test"메소드가 호출될 때마다 새로운 인스턴스를 생성하여 독립적 테스트를 수행한다.
- assertXXXX 메소드로 테스트 케이스의 수행 결과를 판단한다.
Assert.fail(message) | 메소드 실행 결과를 실패로 만듦, 테스트 코드를 구현하기 전에 placeholder로 활용 가능 |
Assert.assertEquals("출력글", a, b) | a와 b가 같은지 테스트한다. 같으면 성공 & 같지 않으면 실패, 실패하면 "출력글"을 출력한다. |
Assert.assertEquals("출력글", a, b, delta) | a와 b가 delta 범위 내에서 같은지 테스트한다. |
Assert.assertArrayEquals(array1, array2) | array1과 array2가 일치하는지 테스트한다. |
테스트 끝날 때마다 데이터 지워주는 코드 : @AfterEach
테스트는 각각 독립적으로 실행되어야 한다. 테스트 순서에 의존관계가 있는 것은 좋은 테스트가 아니다.
@AfterEach
public void afterEach() {
memberRepository.clearStore();
}
테스트 어노테이션 정리
- @Test : 해당 메소드가 테스트 대상 메소드임을 의미
- @Test(timeout=10) : 테스트 메소드 수행 시간이 10 millisecond를 넘기면 실패
- @Test(expected=RuntimeException.class) : RuntimeException이 발생하면 테스트 성공, 아니면 실패
- @Before 메소드 : 각 테스트 메소드에서 공통적으로 실행해야 할 작업 포함, 테스트 메소드가 실행되기 전에 실행됨
- @After 메소드 : 각 테스트 메소드의 실행이 완료된 후에 동일한 마무리 작업을 수행할 경우 사용, 테스트 메소드가 실행된 후에 실행됨
- @BeforeClass 메소드 : static 메소드이며, 모든 테스트 메소드가 동작하기 전 공통적으로 실행해야 하는 작업 수행
- @AfterClass 메소드 : static 메소드이며, 모든 테스트 메소드가 생행되고 난 후에 한번 실행하는 메소드
- @BeforeEach : 각 테스트 실행 전에 호출된다. 테스트가 서로 영향이 없도록 항상 새로운 객체를 생성하고,
의존관계도 새로 맺어준다.
회원 서비스 테스트 코드 예시
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
@BeforeEach
public void beforeEach() {
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
@AfterEach
public void afterEach() {
memberRepository.clearStore();
}
@Test
public void 회원가입() throws Exception {
//Given
Member member = new Member();
member.setName("hello");
//When
Long saveId = memberService.join(member);
//Then
Member findMember = memberRepository.findById(saveId).get();
assertEquals(member.getName(), findMember.getName());
}
@Test
public void 중복_회원_예외() throws Exception {
//Given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//When
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class,
() -> memberService.join(member2));//예외가 발생해야 한다.
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
}
참고
[강의] 인프런 김영한님스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술