개발자가 자신이 만든 코드가 아닌 다른 사람이 만든 코드와 기능에 대한 테스트를 작성할 필요가 있을까?
일반적으로 자신이 만들고 잇는 코드에 대한 테스트만 작성하지만 때로는 자신이 만들지 않은 프레임워크나 다른 개발팀이 만든 라이브러리 등에 대해서도 테스트를 작성해야 한다. 이것을 학습 테스트learning test라고 한다.
학습 테스트의 목적은 자신이 사용할 API나 프레임워크의 기능을 테스트로 보면서 사용방법을 익히려는 것이다.
- 학습 테스트의 장점
1. 다양한 조건에 따른 기능을 손쉽게 확인해볼 수 있다
학습 테스트는 자동화된 테스트 코드로 만들어지기 때문에 다양한 조건에 따라 기능이 어떻게 동작하는지 빠르게 확인할 수 있다.
2. 학습 테스트 코드를 개발 중에 참고할 수 있다
다양한 기능과 조건에 대한 테스트 코드를 개별적으로 만들고 남겨둘 수 있다. 이렇게 테스트로 새로운 기술의 다양한 기능을 사용하는 코드를 만들어두면 실제 개발에서 샘플 코드로 참고할 수 있다. 미리 만들어진 다양한 기능에 대한 테스트 코드가 좋은 참고 자료가 된다.
3. 프레임워크나 제품을 업그레이드할 때 호환성 검증을 도와준다
오픈소스 프레임워크뿐 아니라 상용 제품도 업데이트가 자주 공개된다. 이때 API 사용법에 미묘한 변화가 생기거나, 잘 작동하던 기능에 문제가 발생할 수도 있다. 이런 경우에는 문제가 없던 이전 버전으로 다운그레이드를 해야 한다. 그런데, 이렇게 적용을 하지 않고도 기존에 사용하던 API가 기능에 문제가 없다는 사실을 학습 테스트를 통해 미리 확인해볼 수 있다.
학습 테스트에 애플리케이션에서 자주 사용하는 기능에 대한 테스트를 만들어놓았다면 새로운 버전의 프레임워크나 제품을 학습 테스트에만 먼저 적용해본다. 버그가 있다면 학습 테스트를 통해 미리 확인할 수가 있다. 테스트에 실패하면 업그레이드 일정을 늦추거나 그에 맞춰 애플리케이션 코드를 수정할 계획을 세울 수 있을 것이다.
4. 테스트 작성에 대한 좋은 훈련이 된다
개발자가 테스트를 작성하는 데 아직 충분히 훈련되어 있지 않거나 부담을 갖고 있다면, 먼저 학습 테스트를 작성해보면서 테스트 코드 작성을 연습할 수 있다. 또한 한두 가지 간단한 기능에만 초점을 맞추면 되기 때문에 테스트도 대체로 단순하다. 따라서 애플리케이션 개발 중에 작성하는 테스트보다 작성에 있어서 수월하고 부담이 적다.
5. 새로운 기술을 공부하는 과정이 즐거워진다.
테스트 코드를 만들면서 하는 학습은 흥미롭고 재미있다.
- 학습 테스트 예제
JUnit 테스트 오브젝트 테스트
JUnit는 테스트 메소드를 수행할 때마다 새로운 오브젝트를 만든다고 했는데 학습 테스트를 만들어서 직접 확인해보자.
코드는 아래와 같다.
assertThat()에서 사용하는 몇 가지 매처가 추가됐다. not()은 뒤에 나오는 결과를 부정하는 매처다. is(not())은 반대로 같지 않아야 성공한다. samaInstance()는 실제로 같은 오브젝트인지를 비교한다.
이 테스트를 실행하면 성공하겠지만 이 방식으로는 직전 테스트에서 만들어진 테스트 오브젝트와만 비교한다는 점이 아쉽다. 좀 더 꼼꼼한 테스트를 위해 테스트 방법을 개선해보자.
스태틱 변수로 테스트 오브젝트를 저장할 수 있는 컬렉션을 만들어둔다. 테스트마다 현재 테스트 오브젝트가 컬렉션에 등록되어 있는지 확인하고 없으면 자기 자신을 추가한다. 이 과정을 반복한다. 이 방법을 이용하면 테스트가 어떤 순서로 실행되는지 상관없이 오브젝트 중복 여부를 확인할 수 있다.
추가적으로 testObject를 출력해보면 3개의 서로 다른 테스트 오브젝트들이 들어 있는 것을 확인할 수 있다.
스프링 테스트 컨텍스트 테스트
이번에는 스프링 테스트 컨텍스트 프레임워크에 대한 학습 테스트를 만들어보자. JUnit와 반대로 스프링의 테스트용 애플리케이션 컨텍스트는 테스트 개수와 상관없이 한 개만 만들어진다고 했는데 검증하는 학습 테스트를 만들어보자.
DI기능을 확인하는 것이 아니므로 아무런 빈이 등록되지 않은 설정 파일을 하나 만들자.
앞에서 만들었던 JunitTest에 @RunWith와 @ContextConfiguration 어노테이션을 추가하고 방금 만든 설정 파일을 사용하는 테스트 컨텍스트를 적용하자.
먼저 context를 저장해둔 스태틱 변수인 contextObject가 null인지 확인한다. null이라면 첫 번째 테스트일 테니 통과시키고 contextObject에 context를 저장해둔다. 다음부터 저장되는 contextObject가 null이 아닐 테니 현재의 context와 비교할 수 있다. 만약 한 번이라도 다른 오브젝트가 나오면 테스트는 실패한다.
이번 테스트에서는 다양한 검증 방법을 연습해볼 수 있도록 각 테스트 메소드마다 다른 방법을 사용했다.
첫 번째 방법은 assertThat()을 이용하는 것이다. 매처와 비교할 대상인 첫 번째 파라미터에 boolean 타입의 결과가 나오는 조건문을 넣는다. 그리고 그 결과를 is() 매처를 써서 true와 비교하면 된다. is() 타입만 일치하면 어떤 값이든 검증할 수 있다.
두 번째 방법은 조건문을 받아서 그 결과가 true인지 false인지를 확인하도록 만들어진 assertTrue()라는 검증용 메소드를 사용했다.
코드가 assertThat() 방식보다는 조금 간결해지는 장점이 있다.
세 번째 방법도 assertThat()을 사용한다. 이번엔 조건문을 넣어서 그 결과를 true와 비교하는 대신 매처의 조합을 이용하는 방법을 사용했다. either()는 뒤에 이어서 나오는 or()와 함께 두 개의 매처의 결과를 OR조건으로 비교해준다. 두 가지 매처 중에서 하나만 true로 나와도 성공이다. nullValue()는 이름 그대로 오브젝트가 null인지를 확인해준다.
- 버그 테스트
버그 테스트bug test란 코드에 오류가 있을 때 그 오류를 가장 잘 드러내 줄 수 있는 테스트를 말한다.
버그가 있다고 알려온 경우 무작정 코드를 뒤져가면서 수정하려고 하기보다는 먼저 버그 테스트를 만들어보는 편이 유용하다.
버그테스트는 일단 실패하도록 만들어야 한다. 버그가 원인이 돼서 테스트가 실패하는 코드를 만드는 것이다. 그다음에 버그 테스트가 성공할 수 있도록 애플리케이션 코드를 수정한다. 테스트가 성공하면 버그가 해결된 것이다.
버그 테스트의 장점
1. 테스트의 완성도를 높여준다
기존 테스트에서 미처 검증하지 못했던 부분이 있기 때문에 오류가 발생한 것이므로 이에 대해 테스트를 만들면 불충분했던 테스트를 보완해 준다.
2. 버그의 내용을 명확하게 분석하게 해 준다
버그가 있을 때 그것을 테스트로 만들어서 실패하게 만들려면 어떤 이유 때문에 문제가 생겼는지 명확히 알아야 한다.
따라서 버그를 좀 더 효과적으로 분석할 수 있다. 그 과정에서 버그로 인해 발생할 수 있는 다른 오류를 함께 발견할 수 있다.
3. 기술적인 문제를 해결하는 데 도움이 된다
때로는 버그가 있어도 그 원인을 정확히 파악하기 힘들 때가 있다. 코드와 설정 등을 봐도 문제가 없는 것처럼 느껴지거나 또는 기술적으로 다루기 힘든 버그를 발견하는 경우도 있다. 이럴 땐 동일한 문제가 발생하는 가장 단순한 코드와 그에 대한 버그 테스트를 만들어보면 도움이 된다. 또한 외부의 도움을 받을 때도 도움이 된다.
- 정리
- 테스트는 자동화돼야 하고, 빠르게 실행할 수 있어야 한다.
- main() 테스트 대신 JUnit 프레임워크를 이용한 테스트 작성이 편리하다.
- 테스트 결과는 일관성이 있어야 한다. 코드의 변경 없이 환경이나 테스트 실행 순서에 따라서 결과가 달라지면 안 된다.
- 테스트는 포괄적으로 작성해야 한다. 충분한 검증을 하지 않는 테스트는 없는 것보다 나쁠 수 있다.
- 코드 작성과 테스트 수행의 간격이 짧을수록 휴과적이다.
- 테스트하기 쉬운 코드가 좋은 코드다.
- 테스트를 먼저 만들고 테스트를 성공시키는 코드를 만들어가는 테스트 주도 개발 방법도 유용하다.
- 테스트 코드도 애플리케이션 코드와 마찬가지로 적절한 리팩토링이 필요하다.
- @Before, @After를 사용해서 테스트 메소드들의 공통 준비 작업과 정리 작업을 처리할 수 있다.
- 스프링 테스트 컨텍스트 프레임워크를 이용하면 테스트 성능을 향상시킬 수 있다.
- 동일한 설정파일을 사용하는 테스트는 하나의 애플리케이션 컨텍스트를 공유한다.
- @Autowired를 사용하면 컨텍스트의 빈을 테스트 오브젝트에 DI할 수 있다.
- 기술의 사용 방법을 익히고 이해를 돕기 위해 학습 테스트를 작성하자.
- 오류가 발견될 경우 그에 대한 버그 테스트를 만들어두면 유용하다.
'Spring > 토비의 스프링 정리' 카테고리의 다른 글
3.3 JDBC 전략 패턴의 최적화, 3.4 컨텍스트와 DI (0) | 2022.04.08 |
---|---|
3.1 다시 보는 초난감 DAO (0) | 2022.04.07 |
2.4 스프링 테스트 적용 (0) | 2022.04.05 |
2.3 개발자를 위한 테스팅 프레임워크 JUnit (2) | 2022.04.04 |
2.2 UserDaoTest 개선 (0) | 2022.04.01 |