[TDD] Test code 접근 방식, Mock, Mockito, BDD
Test Code
Test Method 명명 방식
1. Test 대상 method와 이름을 1:1로 매치
- getName()-> test_getName()
2. 1:1로 매치 후 메소드명에 예상 동작 표현
- getName() -> test_getName_isSuccess()
3. 테스트 시나리오에 맞게 정하기
접근 방식
1. 시나리오식 접근 방법
1)Happy Day 시나리오
- 정상적인 흐름일 때, 동작해야 하는 결과값 선정
2)Blue Day 시나리오
- 예외나 에러 상황에 대한 결과값 선정
2. 삼각측량법
- 곱하기 메소드의 경우 (a*b)와 같은지, a를 b번 더한 것과 같은지 확인하는 방법
3. Edge Case
- 경계 조건의 값들로 테스트하는 방법
Mock
Mock 객체
- 모듈의 겉모양이 실제 모듈과 비슷하도록 만든 가짜 객체
- ‘의존성’ -> 테스트 작성의 어려움 -> 의존성을 단절시키기 위해 Mock 객체 사용
1. 사용 이유
1) 테스트 작성을 위한 환경 구축의 어려움
- 환경 구축을 위한 작업 시간이 많이 필요한 경우
- 특정 모듈을 갖고 있지 않아 테스트 환경 구축이 어려운 경우
- 타 부서의 협의나 정책이 필요한 경우
2) 테스트가 특정 경우나 순간에 의존적
- 특정 상태를 가상으로 만들어 놓을 수 있음
3) 테스트 시간이 오래 걸릴 때
Test Double
- 원래의 객체를 사용해서 테스트 진행이 어려운 경우, 이를 대신할 수 있게 만들어 주는 객체
1. Dummy Object
- 인스턴스화 될 수 있을 수준으로만 인터페이스를 구현한 객체
- 해당 객체의 기능이 필요없을 때 사용
2. Test Stub
- Dummy object가 실제로 동작하는 것처럼 보이게 만든 객체
- 특정 상태를 가정해서 만듬
3. Fake Object
- 여러 개의 인스턴스를 대표할 수 있는 객체
- 좀 더 복잡한 구현이 들어가있는 객체
- DB에서 가져온 정보처럼 data를 가지고 있음
4. Test Spy
- 특정 메소드 정상 호출 여부 확인
- 보통 Mock Framework 이용
- EasyMock, jMock, Mockito에 기본적으로 포함되어 있음
5. Mock Object
- 행위를 검증하기 위해 사용되는 객체(행위 기반 테스트)
- 보통 test double과 mock object를 같은 의미로 사용함
Mock Framework
- Mock Object를 직접 생성할 필요없게 도와주는 framework
- Mock 객체에 대한 행위도 테스트 케이스에 포함시킬 수 있음
1. Mockito
- Mock Framework 중 하나
1) 장점
- 테스트의 행위와 반응에만 집중해서 테스트 메소드 작성할 수 있게 해줌
- 테스트 스텁을 만드는 것을 쉽게 하고 만드는 것과 검증을 분리
- Mock을 만드는 방법을 단일화
- API가 간단
- 프레임워크가 지원하지 않으면 안되는 코드를 최대한 배제
- 에러추적이 깔끔
2) 과정
- Mock 생성 -> Stub -> 수행 -> 검증
- Mock 생성
@Test
public void test_getName_isOk() throws Exception{
@Data
@AllArgsConstructor
@NoArgsConstructor
class Test {
private int id;
private String msg;
}
Test testMock = Mockito.mock(Test.class);
System.out.println(testMock.getId()); // -> 0
System.out.println(testMock.getMsg()); // -> null
}
- Stub : Mockito.when으로 특정 메소드가 호출 됐을 때, .thenReturn(return값)을 지정함
@Test
public void test_getName_isOk() throws Exception{
@Data
@AllArgsConstructor
@NoArgsConstructor
class Test {
private int id;
private String msg;
}
Test testMock = Mockito.mock(Test.class);
System.out.println(testMock.getId()); // -> 0
System.out.println(testMock.getMsg()); // -> null
Mockito.when(testMock.getId()).thenReturn(1);
Mockito.when(testMock.getMsg()).thenReturn("MockTest");
System.out.println(testMock.getId()); // -> 1
System.out.println(testMock.getMsg()); // -> "MockTest"
}
-> 결과
- 검증 : Mockto.verify를 사용하여 특정 mock에 대해 어떤 조건을 만족하며 method를 호출했는지 검증
@Test
public void test_getName_isOk() throws Exception{
@Data
@AllArgsConstructor
@NoArgsConstructor
class Test {
private int id;
private String msg;
}
Test testMock = Mockito.mock(Test.class);
System.out.println(testMock.getId()); // -> 0
System.out.println(testMock.getMsg()); // -> null
Mockito.when(testMock.getId()).thenReturn(1);
Mockito.when(testMock.getMsg()).thenReturn("MockTest");
System.out.println(testMock.getId()); // -> 1
System.out.println(testMock.getMsg()); // -> "MockTest"
Mockito.verify(testMock,Mockito.never()).getId(); // 검증
}
-> 결과
- spy 사용 : 일반 객체를 그대로 가져옴
@Test
public void test_getName_isOk() throws Exception{
@Data
@AllArgsConstructor
@NoArgsConstructor
class Test {
private int id;
private String msg;
}
Test test = new Test(10,"test");
Test testMock1 = Mockito.spy(test);
System.out.println(testMock1.getId()); // -> 10
System.out.println(testMock1.getMsg()); // -> "test"
}
-> 결과
- SMART_NULL 사용 : RETURNS_SMART_NULLS 사용 시, int는 0, string은 "" 값이 들어감
@Test
public void test_getName_isOk() throws Exception{
@Data
@AllArgsConstructor
@NoArgsConstructor
class Test {
private int id;
private String msg;
}
Test testMock2 = Mockito.mock(Test.class,Mockito.RETURNS_SMART_NULLS);
System.out.println(testMock2.getId()); // -> 0
System.out.println(testMock2.getMsg()); // -> ""
}
-> 결과
BDD(Behavior-Driven Development)
- TDD에 대한 시작점, 범위, 테스트 케이스 등에 대한 문제를 해결해 줄 수 있는 방식
- 어플리켕션의 행위 중 가치 있는 기능부터 개발하여 테스트하는 방식
- TDD(Test-Driven Development) : inside-out 방식(안쪽 모듈 -> 사용자에 가까운 부분)
-> unit test 지향
- BDD : outside-in 방식(사용자에 가까운 부분 -> 안쪽 모듈)
-> 사용자 테스트, 회귀 테스트 지향
TDD와의 차이점
1. BDD는 사용자에 좀 더 가까운 고수준의 기능영역을 우선적으로 다룸
2. 무엇을 테스트할 것인가에 초점이 맞추고 있음
3. Test method 작성에 용이한 문장적인 템플릿 제공
- Given(조건), When(기능 수행), Then(예상 결과)
@Test
public void test_getName_isOk() throws Exception{
@Data
@AllArgsConstructor
@NoArgsConstructor
class Test {
private int id;
private String msg;
}
Test test = BDDMockito.mock(Test.class);
System.out.println(test.getId());
BDDMockito.when(test.getId()).thenReturn(10);
System.out.println(test.getId());
}
- Spring boot에 있는 BDDMockito 사용
- when으로 특정 method 지정 시, 해당 method를 호출 할 때, 해당 값을 return하도록 설정
-> 결과
Reference) https://repo.yona.io/doortts/blog/issues