현재 문제점
- 테스트 코드가 어느 정도 깔끔해지긴 했지만 어플리케이션 컨텍스트의 생성 방식에 문제가 있다. @Before 메소드가 테스트 메소드 개수만큼 반복되기 때문에 컨텍스트도 여러 번 생성된다.
- 빈이 많아지고 복잡해지면 애플리케이션 컨텍스트 생성에 적지 않은 시간이 걸린다. 또 한 초기화 될 때 어떤 빈은 독자적으로 많은 리소스를 할당하거나 독립적인 스레드를 띄우기 때문에 테스트를 마칠 때마다 애플리케이션 컨텍스트 내의 빈이 할당한 리소스 등을 정리해주지 않으면 다음 컨텍스트를 만들 때 문제가 생길 수 있다.
- 애플리케이션 컨텍스트의 경우 초기화되고 나면 내부의 상태가 바뀌지 않기 때문에 한 번만 만들고 여러 테스트가 공유해서 사용하도록 하는 것이 좋다. 이를 위해 스프링이 직접 제공하는 애플리케이션 컨텍스트 테스트 지원 기능을 사용하자.
스프링의 테스트
스프링을 테스트할 때는 @SpringBootTest 어노테이션을 붙여주고 DI가 필요한 객체에는 Autowired를 붙여주면 된다.
@SpringBootTest
class UserDaoTest {
@Autowired private UserDao dao;
}
dao에 실제 입력된 객체가 없는데, 어떤 식으로 사용이 되는지 한 번 확인해보자.
@SpringBootTest
class UserDaoTest {
@Autowired private UserDao dao;
@BeforeEach
public void seteUp() {
System.out.println(this.dao);
System.out.println(this);
}
@Test
public void addAndSet() throws SQLException, ClassNotFoundException {}
@Test
public void addAndSet() throws SQLException, ClassNotFoundException {}
@Test
public void count() throws SQLException, ClassNotFoundException {}
@Test
public void getUserFailure() throws SQLException, ClassNotFoundException {}
dao는 세 번 모두 동일하게 나와있는데, 스프링 컨테이너가 UserDao 자체를 싱글톤으로 관리하고 주입해주기 때문이다.
UserDaoTest 오브젝트는 매번 주소 값이 다르다. 앞에서 설명한 것처럼 JUnit은 테스트 메소드를 실행할 때마다 새로운 테스트 오브젝트를 만들기 때문이다.
@Autowired
@Autowired가 붙은 인스턴스 변수가 있으면, 테스트 컨텍스트 프레임워크는 변수 타입과 일치하는 컨텍스트 내의 빈을 찾고 타입이 일치하는 빈이 있으면 인스턴스 변수에 주입해준다. 또한 변수에 할당 가능한 타입을 가진 빈을 자동으로 찾는데, 이 때문에 인터페이스 타입으로 선언해도 사용이 가능하다.
그렇다면 동일한 타입이 두 개 이상 있는 경우에는 어떻게 빈을 가져올까? @Autowired는 타입으로 가져올 빈 하나를 선택할 수 없는 경우에는 변수의 이름과 같은 이름의 빈이 있는지 확인한다.
그렇다면 구현체 클래스 이름으로 변수를 선언하는 방법과 인터페이스 타입으롤 변수를 선언하는 방법 중 어떤 것이 나을까? → 테스트에서 빈을 어떤 용도로 사용하느냐에 따라 다르다.
- DataSource에 정의된 메소드를 테스트에서 사용하고 싶다면 DataSource 타입으로 받는게 좋다.
- DatsSource로 선언하면 dataSource 빈의 구현 클래스를 변경하더라도 테스트 코드를 수정할 필요가 없다.
- 테스트에서 SimpleDriverDataSource라는 타입의 오브젝트 자체에 관심이 있는 경우에는 구현체로 선언하는 것이 좋다.