좋은 테스트를 위한 F.I.R.S.T 원칙
테스트코드를 작성할 때 10의 9은 사용하는 패턴이다.
given : 시스템의 설정
when : 시스템이 수행할 작업
then : 결과를 검증
JUnit : Java 에서 사용되는 대표적인 테스트 프레임워크. 자바 개발자 93% 가 사용한다는 통계가 있습니다. Assertion을 이용하여 예상하는 값과 실제 도출된 값을 검증할 수 있는 기능을 지원한다.
Mockito : Mock 객체를 쉽게 만들고, 관리하고, 검증할 수 있는 방법을 제공하는 프레임워크 입니다. Mock 객체는 진짜 객체와 비슷하게 동작하지만 프로그래머가 직접 행동을 관리하는 객체.
@Mock: Mock 객체를 만들어 반환해주는 어노테이션
@InjectMocks: @Mock 또는 @Spy로 생성된 가짜 객체를 자동으로 주입시켜주는 어노테이션, Mock을 가져와서 사용하는 레파지토리. 이건 그럼 실체가 있음 Mock객체가 아님
@MockBean: 기존에 사용되던 Bean의 껍데기만 가져오고 내부의 구현 부분은 모두 사용자에게 위임 (Spring 에 자동으로 Bean 으로 주입됨)
given에서 선언한 코드 외에는 전부 사용할 수 없다
@Spy: Stub하지 않은 메소드들은 원본 메소드 그대로 사용하는 어노테이션
@SpyBean: given에서 선언한 코드 외에는 전부 실제 객체의 것을 사용합니다.
예를 들어 UserController에 대한 단위 테스트를 작성하고자 할 때, UserService를 사용하고 있다면 @Mock 어노테이션을 통해 가짜 UserService를 만들고, @InjectMocks를 통해 UserController에 이를 주입시킬 수 있는 것.
Mock기반으로 작성된 테스트 코드는 인터넷이 안되는 비행기 안에서도 동작 및 검증 가능 (제약사항이 없기 때문에 매우 빠르게 동작)
Mock - 가짜 깡통. 의존성 주입으로 독립성 유지할때 사용. 동장을 전부다 직접 지정해줘야한다.
Spy - 진짜 객체처럼 쓸수도 있고 원할때 동작을 지정해서 쓸 수도 있다.
InjectMock - 우리가 아는 진짜 객체
좋은 테스트코드란 미래의 나를 위한 것이다.
순간순간 테스트가 아니라 결과 상태가 같은 테스트를 작성해야 한다
간결한 코드! 테스트에서 가장 중요한 건 가독성!
유지보수하기 쉬운 단위 테스트가 생산성을 높여주는 테스트이다.
버그를 조기에 발견할 수 있고, 리팩토링을 안전하게 할 수 있다. 배포 전 검증을 빠르게 마무리 할 수 있다.
깨지기 쉬운 테스트를 예방하기
테스트 이름은 검사하는 행위에 어울리게 짓기
테스트에 논리를 넣지 말자
클래스 내부에서만 사용하는 private 메서드에 관한 건 테스트할 필요 없다.
public 메서드의 if문은 에러 메세지 잘 나오나 테스트하면 좋음. 하지만 변수명이나 메서드에 연연하면 안됨
➕ Given과 When은 둘 다 동작은 동일한데 스타일이 다름.
given은 BDD(행위 주도 개발) 스타일에 더 맞게 나온 거라서, BDD의 포맷이 given-when-then이다 보니 거기에 맞게 given을 사용한다.
가독성을 올리기 위한 전략이다.
모든 테스트에서 기능을 확인 하기 위해서 셋팅해줘야 하는 파라미터 값, 변수를 매번 호출 할 필요가 없다. 또한 이후 엔티티나 어떤 값이 변경 되더라도 쉽게 대응 할 수 있다.
공유데이터를 사용했더니 길었던 given이 간결하게 표현됐다.

given(managerRepository.existsManagerByUser_IdAndTodo_id(authUser.getId(),todo.getId())).willReturn(false);
❓ 이 테스트 코드에서 파라미터에 아무 값이나 넣어줘도 false가 나오게 하는건데
이렇게 하면 왜 테스트를 하는거지? 왜 given으로 객체들을 생성해 주는거지? 아무거나 넣어도 false가 되게 만들었는데?!
➡️ given() 메서드는 메서드 호출 시 실제 데이터베이스를 호출하지 않고, 미리 정의된 값(true 또는 false)을 반환하도록 설정한다.
이 테스트코드는 값이 맞지 않을 경우에 예외처리가 되는지 확인하는 것이다.
그래서 우리가 궁금한건 맞지 않을 경우에 예외처리가 되는지!!! 이다.
어떤 값을 넣든 그건 안궁금하고, 맞지 않을 때 예외처리가 잘 되는지!!!! 이다.
그래서 안에 넣어주는 값은 신경쓰지 않고 false일 때 예외처리가 잘 되는지 신경쓰는 것에 집중하는 것이다.
❓ 오케이 그러면 객체를 왜 넣어줘야돼 그냥 바로
given(managerRepository.existsManagerByUser_IdAndTodo_id(anyLong(),anyLong())).willReturn(false);
만 주고 테스트하면 되는거 아니야?!
➡️
given(todoRepository.findById(anyLong())).willReturn(Optional.of(todo));
commentService.saveComment(authUser, todo.getId(), request));
이 두 코드에서 객체를 사용하기 위한 것도 있고, 테스트 환경에서 현실적인 상황을 시뮬레이션하기 위해 생성되고 설정된다. 테스트에서 중요한 상호작용을 담당하며, Todo가 어떤 할일인지, User가 어떤 사용자인지 정의하는 것!
현실적인 시나리오를 만들고, 그 시나리오에서 올바른 예외가 발생하는지를 검증하기 위해 필요하다!