테스트
스프링이 개발자에게 제공하는 것 중 가장 중요한 가치가 무엇이냐 하면 객체지향과 테스트다.
앱은 변화가 계속되는데, 이런 변화에 대응하는 첫 전략이 확장과 변화를 고려한 객체지향 설계와 이걸 효과적으로 쓰게 해주는 IOC/DI 기술이고,
두번째 전략은 코드를 확실하게 해주는 테스트 기술이다.
UserDaoTest 다시 보기
테스트의 유용성
테스트 코드를 통해 코드가 제대로 작동하는지 확인 할 수 있다.
코드의 결함을 제거해 나가고 디버깅을 거쳐 결함이 제거 된걸 확인 할 수 있다.
UserDaoTest특징
다시 1장에서 작성한 UserDao 테스트의 장점을 간단하게 봐보자.
public class UserDaoTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
final ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
final UserDao userDao = context.getBean("userDao",UserDao.class);
User user = new User();
user.setId("testId");
user.setName("테스트");
user.setPassword("test");
userDao.add(user);
System.out.println(user.getId() + " 등록 성공");
User user2 = userDao.get(user.getId());
System.out.println(user2.getName());
System.out.println(user2.getPassword());
System.out.println(user2.getId() + " 조회 성공");
}
}
위 처럼 테스트 코드를 작성하면 장점이 있다.
- 손쉽게 실행가능한 main() 함수를 사용해서 테스트 할 수 있다.
- 테스트 결과를 콘솔로 바로 볼 수 있다.
- 사용할 객체들의 의존성을 쉽게 주입 해 줄 수 있다.
웹을 통한 테스트의 문제점
테스트코드가 아니라 웹을 통해 테스트 하려면 서비스, 컨트롤러 뷰 를 다 만들어봐야 테스트를 할 수 있다는 단점이 있다.
작은 단위의 테스트 필요성
너무 많은 것을 한번에 테스트하면 수행과정도 복잡하고 원인을 찾기도 힘들어진다.
기능 단위로 쪼개서 테스트에 집중 하는게 좋다
이렇게 작은 단위의 코드에 대해 테스트를 수행한 것을 단위테스트 라고 한다.
자동 수행 테스트 코드
테스트를 자동으로 수행되게 만들어두는건 중요하다.
자동으로 수행되는 테스트는 자주 반복할 수 있고 언제든 코드를 수정하고 나서 테스트를 해볼 수 있다.
간단한 수정이라도 심각한 문제가 발생 할 수 있는데 이럴때 만들어둔 테스트가 있다면 수정 후 빠르게 전체 테스트를 해서 다른 기능에 문제가 발생하진 않는지 확인 해볼 수 있다.
UserDaoTest의 문제점
위처럼 테스트코드를 작성하는게 웹에서 직접 테스트하는 것보다 훨씬 효율적이지만,
테스트 코드를 개발자가 직접 실행해야하고 직접 결과를 확인해야한다는 부분이 안좋다.
그래서 이런 문제를 해결할 수 있게 Junit 테스트 프레임워크를 스프링에서 제공해준다.
UserDaoTest 개선
모든 테스트는 성공과 실패로 나뉘는데 실패중에서도
로직에 에러가 나서 실패하는 경우와 에러는 발생하지 않지만 기대한 값과 결과가 다른 경우로 구분 할 수 있다.
원하는 값이 나왓는지 메시지를 출력하도록 해서 자동화 시킬 수 있다.
테스트의 효율적인 수행과 결과 관리
Junit테스트로 전환
프레임워크는 IOC 로 클래스에 대한 제어 권한을 받아서 흐름을 제어한다.
클래스를 생성하고 실행하는 일은 프레임워크에 의해 진행되는데
main()메소드로 만든 테스트는 이런면에서 프레임워크에 적용하기엔 적합하지 않다.
Junit을 이용해서 다시 작성해보자
Junit 으로 테스트를 만들기 위해선 2가지 조건이 있다.
1. public 으로 선언
2. 메소드에 @test 애노테이션 붙이기
검증 코드 전환
if/else 문으로 테스트 결과를 검증하던 부분을 assertThat으로 쉽게 변경이 가능하다.
assetThat 메소드는 첫 파라미터를 뒤에 나오는 matcher 조건과 비교해서 일치하면 다음으로 넘어가고 아니면 테스트를 실패 시키는 메소드다.
여기서 is()는 matcher 종류 중 하나로 equal로 비교해주는 메소드다.
assertThat(user2.getPassword(), is(user.getPassword()));
개발자를 위한 테스팅 프레임워크 JUnit
테스트 결과의 일관성
테스트가 외부 상황에 따라 결과가 달라지는 문제가 있을 수 있다.
하지만 테스트는 코드에 변경사항이 없다면 항상 동일한 결과를 내야 한다.
addAndGet 같은 메소드를 마치고 사용자 정보를 삭제해서 테스트 수행하기 이전 상태로 만들어 주는 방법이 있다.
테스트는 실행 순서에 상관없이 독립적으로 항상 동일한 결과를 내야 한다.
테스트 코드를 잘 작성하면 잘 작성된 기능정의서 처럼 보이게 된다.
테스트 주도 개발(TDD)
기능을 테스트 코드를 먼저 만들고 테스트 성공한 코드를 작성하는 방식의 개발 방식을 TDD 라고 한다.
테스트 코드 개선
테스트 코드도 리팩토링을 할 수 있다.
UserDaoTest 에서 반복되는 부분이 있는데
ApplicationContext ac = new GenericXmlApplicationContext("applicationContext.xml");
UserDao dao = ac.getBean("userDao", UserDao.class);
중복된 코드는 별도의 메소드로 뽑아낼 수 있다.
@Before
Junit이 테스트를 가져와서 테스트를 수행하는 방식
- 테스트 클래스에서 @Test가 붙은 public 에 void 반환 형이고 파라미터가 없는 테스트 메소드를 모두 찾는다.
- 테스트 클래스 객체를 생성
- @Before 가 붙은 메소드가 있으면 실행한다.
- @Test 가 붙은 메소드를 호출 하고 테스트 결과를 저장
- @After 가 붙은 메소드가 있으면 실행한다.
- 나머지 테스트 메소드에 대해 2~5번 반복
- 모든 테스트의 결과를 종합해서 돌려준다.
@Before 는 모든 @Test 붙은 메소드 실행 전에 자동으로 실행한다.
스프링 테스트 적용
@Before 메소드는 테스트 메소드 개수만큼 반복되기 때문에 어플리케이션 컨텍스트도 메소드 개수만큼 만들어진다.
어플리케이션 컨텍스트 생성은 적지 않은 시간이 걸릴 수 있다.
테스트는 가능한한 매번 새로운 오브젝트를 만들어서 하는게 원칙이지만 어플리케이션 컨텍스트처럼 생성에 많은 시간과 자원이 드는 경우엔 스태틱 필드에 저장해두고 공통 변수로 사용을 하면 효율적으로 사용할수있다.
Junit은 테스트 클래스에 걸쳐 딱 한번만 실행되는 @BeforClass 스태틱 메소드도 지원하지만,
이거보다는 스프링이 직접 제공해주는 어플리케이션 컨텍스트 기능을 사용하는게 좋다.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={DaoFactory.class})
public class UserDaoTest{
@Autowired
private ApplicationContext context;
@Before
public void setUp(){
this.dao = this.context.getBean("UserDao",UserDao.class);
...
}
}
@RunWith는 Junit 프레임워크의 테스트 실행 방법을 확장할때 사용하는 어노테이션이고
@RunWith의 SpringJUnit4ClassRunner는 junit용 테스트 컨텍스트 프레임워크 확장 클래스를 지정해주면
Junit 이 사용할 어플리케이션 컨텍스트를 만들고 관리해준다.
@ContextConfiguration은 자동으로 만들을 어플리케이션 컨텍스트 설정위치를 지정한다.
정리
- 테스트 주도 개발은 장점이 많다. 리팩토링, 변경이 있더라도 간단하게 테스트 가능
- api 테스트는 서버 로딩 시간등 여러 시간들이 걸리지만 unit 테스트는 몇개의 빈만 올려서 테스트 할 수 있어서 개발 속도를 높여준다.
- 테스트 주도 개발을 하다보면 자연스럽게 SOLID 원칙을 지키게 되는 경우가 있다. 테스트 하기 어렵다면 SOLID 원칙을 위반하고 있는게 아닌지 의심해봐야한다.