-
3. 유닛 테스트를 어노테이션과 함께 만들 수 있다스프링개발자/202 - 디버깅+테스팅 2020. 8. 14. 10:53
내 어플리케이션의 코드가 꽤 많은 상황에서, 몇가지 기능을 추가 구현했는데, 기존 코드가 작동이 안된다면?
실제로 많이 일어나는 문제이다. 이전에 작동 하던 것은 여전히 작동하고, 새로 동작하는 것 또한 잘 작동하도록 하는것이 테스트의 역할이고 테스트가 중요한 이유이다. 가장 작은 테스트 단위인 유닛 테스트를 만들어보자.
이전 글과 마찬가지로, 여기의 코드베이스를 사용한다; (203시리즈에서 만든 코드이다)
https://github.com/2ndPrince/routeOptimization/pull/8
1. DispatchService의 유닛 테스트를 만들자
@Service public class DispatchService { public void validate(DispatchRequest request) { List<ValidationError> errorList = new ArrayList<>(); request.getRides().stream() .flatMap(r -> Stream.of(r.getPickup(), r.getDropOff())) .collect(Collectors.toSet()) .forEach(location -> validateLocations(location, errorList)); if(!errorList.isEmpty()){ throw new ValidationException(errorList, request.getId()); } } public void validateLocations(Location location, List<ValidationError> errors){ Double longitude = location.getLongitude(); Double latitude = location.getLatitude(); boolean isLonValid = (-180d <= longitude) && ( longitude <= 180d); boolean isLatValid = (-90d <= latitude) && ( latitude <= 90d); if(!isLatValid) errors.add(new ValidationError(ValidationError.Type.BAD_RIDE, "Location", "Invalid Lat Location. Check your Lat", 1)); if(!isLonValid) errors.add(new ValidationError(ValidationError.Type.BAD_RIDE, "Location", "Invalid Lon Location. Check your Lon", 1)); } }
본 서비스는 이렇게 생겼고, 우리는 validateLocations 메소드에 대한 유닛 테스트를 만들어보자. 테스트 클래스들은 보통 test directory의 같은 패키지명 아래 존재한다. DispatchService에서 인텔리제이 단축키 "Ctrl+Shift+T" 를 눌러 테스트 클래스 자동 생성. Junit5를 선택한다.
DispatchServiceTest 클래스의 내용은 다음과 같다;
package me.ndPrince.routeOptimization; import me.ndPrince.routeOptimization.model.ValidationError; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.assertEquals; @ExtendWith(MockitoExtension.class) class DispatchServiceTest { @InjectMocks private DispatchService dispatchService; @Test public void test_validateLocations(){ List<ValidationError> errorList = new ArrayList<>(); Location l1 = new Location(-501.2, 5.3); dispatchService.validateLocations(l1, errorList); assertEquals(1, errorList.size()); } }
어노테이션에 ctrl+click을 하여 나온 javadoc의 정보는 다음과 같다;
1) @ExtendWith(MochkitoExtension.class) : mock을 initialize 해주고, stubbing을 담당하는 익스텐션이다. JUnit4의 MockitoJUnitRunner와 같은 버전의 Junit5 기능이다
2) @InjectMocks: Mockito가 주도적으로 가짜객체를 주입하려고 시도한다. (스프링의 IoC와 비슷하지만 테스트를 위한 Mock 버전이라 생각하면 된다). 생성자를 이용한 객체 주입, Setter를 이용한 주입, 혹은 property를 이용한 주입을 이 순서로 시도한다. 이 시도중의 하나라도 실패한다고 해도, Mockito가 테스트가 실패한건 아니다. 다만, 개발자가 직접 dependency를 제공해야 한다.
Mockito가 주입시켜준 객체를 사용하여 우리가 유닛테스트를 만들고 싶은 메소트를 호출하였다.
dispatchService.validateLocations(l1, errorList);
2. 테스트 구분용 어노테이션을 만들자
DispatchServiceTest는 로직을 테스트 하였다. @LogicTest 라는 어노테이션을 만들고, 여기에 붙여준다.
어노테이션을 만드는 자세한 글은 https://2ndprince.tistory.com/24를 참고한다
package me.ndPrince.routeOptimization.annotation; import org.junit.jupiter.api.Tag; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Tag("Logic") @Retention(RetentionPolicy.RUNTIME) public @interface LogicTest { }
@ExtendWith(MockitoExtension.class) @LogicTest class DispatchServiceTest { @InjectMocks private DispatchService dispatchService;
3. LogicTest를 위한 그레이들 Task 를 만들자
자세한 내용은 해당 글 참고; https://2ndprince.tistory.com/23
build.gradle에 아래의 task를 추가한다
task logicTest(type: Test){ group "demo test" useJUnitPlatform{ includeTags 'Logic' } }
4. 터미널을 이용하여 @LogicTest가 붙은 모든 클래스의 유닛테스트를 실행한다
./gradlew -q logicTest
-q 옵션을 주면, 테스트 run이 실패했을때만 알려준다. BUILD SUCCESSFUL in 1s 같은 log 메세지를 보고 싶으면 -q를 지우고 입력한다.
5. 명령어 동작 확인
위의 명령어가 정말 logicTest 어노테이션이 붙은 클래스를 테스트 했는지 확인하고 싶었다. 아마 더 좋은 방법이 있겠지만, 원시적으로, 일부러 앞서 만든 유닛테스르를 실패하게 assertEquals의 기대값을 1에서 2로 바꾸었다.
실패를 통해 위의 명령어가 잘 작동함을 확인했다.
어노테이션을 통한 테스트 구분은 어플리케이션이 커질수록 정말 필요한 기능이다.
또한 명령어를 통해 실행 시킬 수 있으면, jenkins의 pipeline stage에서도 쉽게 실행 시킬 수 있다.
완성된 코드
https://github.com/2ndPrince/routeOptimization/tree/create-unit-test
다음 시간에는 End-to-End 테스트를 구현해보자.
'스프링개발자 > 202 - 디버깅+테스팅' 카테고리의 다른 글
5. Java + Selenium 테스트를 만들 수 있다 (0) 2020.08.15 4. End-to-End 테스트를 만들 수 있다 (0) 2020.08.15 2. 인텔리제이(IDE)를 써서 효과적으로 디버깅 할 수 있다 (0) 2020.08.14 1. Auto-configure 을 이해하고 디버깅 할 수 있다 (0) 2020.08.14 스프링개발자 202 (0) 2020.07.14