스프링이 개발자에게 제공하는 가장 중요한 가치 = 객체지향과 테스트
2.1.1 테스트의 유용성
테스트
- 의도했던 대로 코드가 정확히 동작하는지 확인해서 만든 코드를 확신할 수 있게 해주는 작업
- 테스트 결과가 원하는대로 나오지 않는 경우 코드에 결함이 있음을 인지하게 해줌
2.1.2 UserDaoTest의 특징
- main() 메소드 이용
- 테스트할 대상의 오브젝트를 가져와 메소드 호출
- 테스트에 사용할 입력값을 직접 코드에서 만들어 넣어줌
- 테스트 결과를 콘솔에 출력
- 각 단계 작업이 끝나면 콘솔에 성공 메세지 출력
웹을 통한 DAO 테스트 방법의 문제점
- 모든 레이어의 기능을 다 만들고 나서 테스트 가능
- 테스트중 에러가 발생하거나 테스트가 실패했다면 어디에서 문제가 발생했는지 찾아야 함
작은 단위의 테스트
테스트는 작은 단위로 쪼개서 할 수 있어야 함
- 관심사의 분리 적용
- 테스트의 관심이 다르다면 테스트 대상을 분리하고 집중해서 접근
단위 테스트(Unit test) : 작은 단위의 코드에 대해 테스트를 수행한 것
- 단위는 작을수록 좋음
- 통제할 수 없는 외부의 리소스에 의존하는 테스트는 단위 테스트가 아니라고 봄
자동수행 테스트 코드
테스트는 자동으로 수행되도록 코드로 만들어지는 것이 중요 테스트 코드는 애플리케이션을 구성하는 클래스안에 포함시키기보다 테스트용 클래스를 별도로 만드는것이 좋음 자동으로 수행되는 테스트의 장점 = 자주 반복할 수 있음
지속적인 개선과 점진적인 개발을 위한 테스트
테스트를 이용하면 새로운 기능도 기대한 대로 동작하는지 확인할 수 있고, 기존에 만들어뒀던 기능들이 새로운 기능을 추가했을 때 잘 동작하는지 확인할 수 있어 점진적인 개발이 가능함
2.1.3 UserDaoTest의 문제점
- 수동 확인 작업의 번거로움
- 콘솔에 값을 출력해주기만 해 결과를 확인하는건 확인하는 사람 책임
- 실행 작업의 번거로움
- 매번 main() 메소드를 실행해야 함
2.2.1 테스트 검증의 자동화
테스트의 결과
- 성공
- 실패 - 테스트 에러 / 테스트 실패
2.2.2 테스트의 효율적인 수행과 결과 관리
JUnit 프레임워크
- 메소드가 public으로 선언되어야 함
- 메소드에
@Test
애노테이션을 붙여주어야 함
검증 코드 전환
if(!user.getName().equals(user2.getName())){}
assertThat(user2.getName(), is(user.getName())); // JUnit의 assertThat 메소드로 변경
- 첫 번째 파라미터 값을 뒤에 나오는 mather 조건으로 비교해서 일치하면 다음으로 넘어가고 아니면 테스트가 실패하도록 만들어 줌
JUnit 테스트 실행
main() 메소드 추가 후 JUnitCore 클래스의 main 메소드를 호출해주는 코드를 작성하면 됨
public static void main(String[] args) {
JUnitCore.main("springbook.user.dao.UserDaoTest");
}
2.3.2 테스트 결과의 일관성
코드에 변경사항이 없다면 실행할 때마다 항상 동일한 테스트 결과를 내야 함 → 테스트하기 전에 테스트 실행에 문제가 되지 않는 상태를 만들어 줌 (테스트 후 테스트가 등록한 사용자 정보를 삭제하는 방식을 쓴다던지 해서..)
포괄적인 테스트
테스트를 작성할 때 부정적인 케이스를 먼저 만드는게 좋음
테스트 주도 개발(TDD, Test Driven Development)
만들고자 하는 기능의 내용을 담고 있으면서 만들어진 코드를 검증도 해줄 수 있도록 테스트 코드를 먼저 만들고, 테스트를 성공하게 해주는 코드를 작성하는 방식의 개발 방법
2.3.5 테스트 코드 개선
@Before
@Test
메소드 실행 전 먼저 실행돼야 하는 메소드 정의- JUnit은
@Test
가 붙은 메소드 실행 전과 후에@Before
와@After
가 붙은 메소드를 자동으로 실행
각 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 확실히 보장해주기 위해 메소드를 실행할 때마다 테스트 클래스의 오브젝트를 새로 만듦
픽스처
- 테스트를 수행하는 데 필요한 정보나 오브젝트
- 일반적으로 여러 테스트에서 반복적으로 사용되기 때문에
@Before
메소드를 이용해 생성
2.4 스프링 테스트 적용
- 애플리케이션 컨텍스트처럼 생성에 많은 시간과 자원이 소모되는 경우 테스트 전체가 공유하는 오브젝트를 만듦
- JUnit은 테스트 클래스 전체에 걸쳐 딱 한번 실행되는
@BeforeClass
메소드 지원
2.4.1 테스트를 위한 애플리케이션 컨텍스트 관리
@RunWith
: JUnit 프레임워크의 테스트 실행 방법을 확장할 때 사용하는 애노테이션- 여러개의 테스트 클래스가 같은 설정파일을 가진 애플리케이션 컨텍스트를 사용한다면 스프링은 테스트 클래스 사이에서도 애플리케이션 컨텍스트 공유
@Autowired
@Autowired
가 붙은 인스턴스 변수가 있으면 변수 타입과 일치하는 컨텍스트 내의 빈을 찾고 타입이 일치하면 인스턴스 변수에 주입- 스프링 애플리케이션 컨텍스트는 자기 자신도 빈으로 등록하기 때문에 DI 가 가능
2.4.2 DI와 테스트
인터페이스를 두고 DI를 적용해야 하는 이유
- 소프트웨어 개발에서 절대로 바뀌지 않는 것은 없기 때문
- 언젠가 변경이 필요한 상황에서 수정에 들어가는 시간과 비용 부담을 줄여줄 수 있음
- 다른 차원의 서비스 기능을 도입할 수 있음
- 새로운 기능을 추가하기위해 기존 코드 수정 X
- 제거할때도 설정파일을 수정해서 제거
- AOP
- 테스트
- 테스트가 작은 다누이의 대상에 대해 독립적으로 만들어지고 실행되게 하는데 중요한 역할을 함
테스트 코드에 의한 DI
@DirtiesContext
- 스프링의 테스트 컨텍스트 프레임워크에게 해당 클래스 테스트에서 애플리케이션 컨텍스트의 상태를 변경한다는 것을 알림
- 수행 후 매번 새로운 애플리케이션 컨텍스트를 만들어 뒤의 테스트에 영향을 주지 않음
테스트를 위한 별도의 DI 설정
- 테스트코드에서 빈 오브젝트에 수동으로 DI 하는 방법 → 매번 애플리케이션 컨텍스트를 새로 만들어야 함
- 테스트에서 사용될 클래스가 빈으로 정의된 테스트 전용 설정파일을 만들어 둠
- 서버 운영용 / 테스트용
컨테이너 없는 DI 테스트
- 스프링 컨테이너를 사용하지 않고 테스트 생성
- 테스트 코드에서 직접 오브젝트를 만들고 DI 해서 사용
- 애플리케이션 컨텍스트를 사용하지 않아 테스트 시간 절약
DI를 이용한 테스트 방법 선택
- 스프링 컨테이너 없이 테스트 할 수 있는 방법을 우선 고려
- 테스트에서 애플리케이션 컨텍스트를 사용하는 경우 테스트 전용 설정파일을 새로 만들어 사용
- 예외적인 의존관계를 구성해서 테스트할 경우 컨텍스트에서 받은 DI 오브젝트에 다시 테스트코드로 수동 DI해서 테스트
2.5 학습테스트로 배우는 스프링
학습테스트(learning test)
- 자신이 사용할 API나 프레임워크의 기능을 테스트로 보면서 사용 방법을 익히는 것
- 테스트를 만들려고 하는 기술이나 기능에 대해 제대로 이해하고있는지, 사용 방법을 알고있즈지 검증하려는 목적
학습테스트의 장점
- 다양한 조건에 따른 기능을 손쉽게 확인해 볼 수 있다
- 자동화된 테스트 코드로 만들어져 다양한 조건에 따라 기능이 어떻게 동작하는지 빠르게 확인 가능
- 학습 테스트 코드를 개발 중 참고할 수 있다
- 새로운 기술의 다양한 기능을 사용하는 코드를 만들어두면 실제 개발에서 샘플 코드로 참고 가능
- 프레임워크나 제품을 업그레이드할 때 호환성 검증을 도와준다
- 테스트 작성에 대한 좋은 훈련이 된다
2.5.3 버그 테스트
버그 테스트 : 코드에 오류가 있을 때 그 오류를 가장 잘 드러내줄 수 있는 테스트
버그 테스트의 필요성과 장점
- 테스트의 완성도를 높여줌
- 버그의 내용을 명확하게 분석하게 해줌
- 기술적인 문제를 해결하는데 도움
동등분할
같은 결과를 내는 값의 범위를 구분해 각 대표값으로 테스트 하는 방법
경계값 분석
에러는 동등분할 범위의 경계에서 주로 발생한다는 특징을 이용해 경계 근처의 값을 이용해 테스트하는 방법
이일민, [토비의 스프링 3.1], 에이콘 출판사(2012)