
JUnit 프레임워크를 이용해서 테스트 코드를 진행한다.
JUnit이 제공하는 기능 중 테스트를 실행할 때마다 반복되는 준비 작업을 별도의 메소드로 빼서 매번 테스트 메소드를 실행하기 전과 후에 실행시켜줄 수 있는 기능이 존재한다.
@Before 애너테이션은 테스트 메소드 실행 전에 먼저 실행되는 기능이다.
매번 테스트 코드를 실행하면서 필요한 변수들을 중복 제거하기 위해서 사용한다.
테스트 메소드에서 객체를 사용해야 되는 경우
클래스의 인스턴스 변수로 선언을 먼저 한 뒤 @Before 애너테이션이 붙은 메서드에서 객체를 생성해 주는 방식으로 사용한다.
인스턴스 변수를 이용하는 이유는 @Before, @After 메소드를 테스트메소드에서 직접 호출하지 않기 때문이다.
private UserDao dao;
@Before
public void setUp() {
ApplicationContext context =
new GenericXmlApplicationContext("applicationContext.xml");
this.dao = context.getBean("userDao", UserDao.class);
}
각 테스트 메소드를 실행할 때마다 테스트 클래스의 오브젝트를 새로 만든다.
각 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 확실히 보장해 주기 위해서 매번 새로운 오브젝트를 만드는 것이다.
픽스처란? 테스트를 수행하는 데 필요한 정보나 오브젝트를 뜻한다.
UserDaoTest를 예시로 들면 dao가 대표적인 픽스처로 볼 수 있다. 테스트 중 add() 메소드에 전달하는 User 오브젝트들도 픽스처라고 볼 수 있다.
UserDao에 대한 테스트라면 대부분 User 오브젝트를 사용할 것으로 예상된다. UserDao의 기능이 계속 만들어지고 그에 따라 테스트 메소드도 계속 추가될 수 있기 때문이다.
픽스처 생성 로직은 흩어져있는 것보다 모여 있는 편이 낫기 때문에 @Before 메소드를 이용한다.
private UserDao dao;
private User user1;
private User user2;
private User user3;
@Before
public void setUp() {
...
this.user1 = new User("gyumee", "박성철", "springno1");
this.user2 = new User("leegw700", "이길원", "springno2");
this.user3 = new User("bumjin", "박범진", "springno3");
위처럼 테스트 코드를 작성하는 경우 @Before 메소드가 테스트 메소드 개수만큼 반복되기 때문에 애플리케이션 컨텍스트도 세 번 만들어진다. 빈이 많아지고 복잡해진다면 애플리케이션 컨텍스트 생성에 적지 않은 시간이 걸릴 수 있다.
애플리케이션 컨텍스트가 만들어질 때 모든 싱글톤 빈 오브젝트를 초기화한다.
그러나 어떤 빈은 오브젝트가 생성될 때 자체적인 초기화 작업을 진행해서 많은 시간을 필요로하거나 애플리케이션 컨텍스트가 초기화될 때 독자적으로 많은 리소스를 할당하거나 독립적인 스레드를 띄우는 문제점이 있다. 이런 경우 테스트를 마칠 때마다 애플리케이션 컨텍스트 내의 빈이 할당한 리소스 등을 깔끔하게 정리해줘야한다.
테스트는 일관성 있는 실행 결과를 보장해야 하고, 테스트의 실행 순서가 결과에 영향을 미치지 않아야 한다.
애플리케이션 컨텍스트는 한 번만 만들고 여러 테스트가 공유해서 사용해도 되기 때문에 @Before 메소드에서 애플리케이션 컨텍스트를 생성하는 코드를 제거해도 된다.
수정 전
@Before
public void setUp() {
ApplicationContext context =
new GenericXmlApplicationContext("applicationContext.xml");
this.dao = context.getBean("userDao", UserDao.class);
}
수정 후
@Runwith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="/applicationContext.xml")
// 톰캣 뜰 때 리스너가 뜨는 거랑 비슷하게 작동
public class UserDaoTest {
@Autowired
private ApplicationContext context; // 싱글톤
...
@Before
public void setUp() {
this.dao = this.context.getBean("userDao", UserDao.class);
}
}
인스턴스 변수인 context는 초기화 해주는 코드가 없지만 애플리케이션 컨텍스트가 들어가 있기 때문에 문제 없이 성공적으로 끝난다.
직접 확인해보기위해 코드를 추가한다.
@Before
public void setUp() {
System.out.println(this.context);
System.out.println(this);
}
실행 결과는 다음과 같다.
org.springframework.context.support.GenericApplicationContext**@d3d6f:**
springbook.dao.UserDaoTest**@115d06c**
org.springframework.context.support.GenericApplicationContext**@d3d6f:**
springbook.dao.UserDaoTest**@116318b**
org.springframework.context.support.GenericApplicationContext**@d3d6f:**
springbook.dao.UserDaoTest**@15e0c2b**
출력된 결과로 보았을 때 context는 세 번 모두 동일하게 나왔다. 하나의 애플리케이션 컨텍스트가 모든 테스트 메소드에서 사용되고 있음을 알 수 있다. 싱글톤 패턴이 적용된 것을 알 수 있다.
반면에 UserDaoTest의 오브젝트는 매번 주소 값이 다르다. JUnit은 테스트 메소드를 실행할 때마다 새로운 테스트 오브젝트를 만들기 때문이다.
@Autowired는 “주입 받는 것” 이다.(DI : 의존성 주입)
주입받는 다는 것은 자주 바뀌는 부분(n개)을 따로 빼서 매개변수로 받아오는 것으로, 기능과 구현을 분리한 것을 뜻한다.
학습 테스트?
예시 1
예시 2
2장에서는 테스트의 필요성과 작성 방법에 대하여 다루었다.