테스트 코드 정리 후, 아직 수정해야 할 부분이 있다.
애플리케이션 컨텍스트 생성 방식이다.
테스트는 가능한 독립적으로 매번 새로운 오브젝트를 만들어서 사용하는 것이 원칙이다.
하지만 애플리케이션 컨텍스트처럼 생성에 많은 시간과 자원이 소모되는 경우에는 테스트 전체가 공유하는 오브젝트를 만들기도 한다.
JUnit의 @BeforClass 스태틱 메소드
하지만 이보다는 스프링이 직접 제공하는 애플리케이션 컨텍스트 테스트 지원 기능을 사용하는 것이 더 편리하다.
스프링은 JUnit을 이용하는 테스트 컨텍스트 프레임워크를 제공한다.
스프링 테스트 컨텍스트 프레임 워크 적용
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationContext.xml")
public class main {
@Autowired
private ApplicationContext context;
...
}
인스턴스 변수인 context는 어디에서도 초기화해주는 코드가 없어도, 해당 테스트는 모두 성공한다.
추가할 라이브러리 : org.springframework.test-3.0.7.RELEASE.jar
이렇게 해서 하나의 테스트 클래스 내의 테스트 메소드는 동일한 애플리케이션 컨텍스트를 공유해서 사용할 수 있음을 확인했다.
테스트 클래스의 컨텍스트 공유
여러 개의 테스트 클래스가 모두 같은 설정파일을 가진 애플리케이션 컨텍스트를 사용한다면,
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationContext.xml")
public class UserDaoTest { ... }
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationContext.xml")
public class GroupDaoTest { ... }
@Autowired
정의
설명
특징
추후에 더 자세히 다룰 예정입니다.
@Autowired 를 이용해 컨텍스트를 가져와 getBean()을 사용하는 것이 아닌, UserDao 빈을 직접 DI 받아보자.
...
public class main {
@Autowired
UserDao userdao;
}
@Autowired를 이용하면 어떤 빈이든 다 가져올 수 있다.
단, @Autowired는 같은 타입의 빈이 두 개 이상 있는 경우에는 타입만으로 어떤 빈을 가져올지 결정할 수 없다.
이런 경우, 해당 인스턴스 변수의 이름과 같은 이름의 빈이 있는지 확인한다.
UserDao와 DB 커넥션 생성 클래스 사이에 DataSource라는 인터페이스를 두었다.
하지만 DataSource의 구현 클래스를 SimpleDriverDataSource로 고정 후 바꾸지 않는다면, DataSource 인터페이스를 사용하고 DI를 통해 주입해주는 방식을 이용해야 할 까?
그래도 인터페이스를 두고 DI를 적용해야 하는 이유가 있다.
테스트 코드에 의한 DI
테스트 코드 내에서 수정자 메소드를 이용해서 직접 DI해도 된다.
애플리케이션이 사용할 applicationContext.xml에 정의된 DataSource 빈은 서버의 DB 풀 서비스와 연결해서 운영용 DB 커넥션을 돌려주도록 만들어져 있다고 해보자.
@DirtiesContext // 테스트 메소드에서 애플리케이션 컨텍스트의 구성이나 상태를 변경한다는 것을 테스트 컨텍스트 프레임워크에 알려준다.
public class main {
...
@Before
public void setUp() {
this.userDao = context.getBean("userDao", UserDao.class);
DataSource dataSource = new SingleConnectionDataSource(
"jdbc:mysql://localhost:3306/testdb?useSSL=false", "id", "pw", true
);
userDao.setDataSource(dataSource); // 수정자 메소드에 의한 수동 DI
장점 : XML 설정 파일을 수정하지 않고도 테스트 코드를 통해 오브젝트 관계를 재구성할 수 있다.
주의할 점 : 애플리케이션 컨텍스트에서 구성된 오브젝트를 가져와 의존관계를 강제로 변경하였다는 것 이다.
이를 해결하기 위해 클래스 레벨에 @DirtiesContext 라는 애노테이션을 추가해줬다.
테스트를 위한 별도의 DI 설정
앞의 방법(테스트 코드에서 빈 오브젝트에 수동으로 DI하는 방법)은 장점보다 단점이 많다.
해당 방법 외에 DI의 장점을 살려서 DAO가 테스트에서만 다른 DataSource를 사용하게 할 수 있는 방법이 있다.
테스트에서 사용될 DataSource 클래스가 빈으로 정의된 테스트 전용 설정파일을 따로 만들어 이용한다.
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/testdb?useSSL=false" />
<property name="username" value="id" />
<property name="password" value="pw" />
</bean>
컨테이너 없는 DI 테스트
스프링 컨테이너를 사용하지 않고 테스트를 만드는 방법이다.
public class main {
private UserDao userDao;
private User user1;
private User user2;
private User user3;
@Before
public void setUp() {
this.userDao = new UserDao();
DataSource dataSource = new SingleConnectionDataSource(
"jdbc:mysql://localhost:3306/testdb?useSSL=false", "root", "2db8ddasf", true
);
userDao.setDataSource(dataSource);
}
}
DI를 이용한 테스트 방법 선택
DI를 테스트에 이용하는 세 가지 방법을 보았다.
세가지 방법 모두 장단점이 있고 상황에 따라 유용하게 쓸 수 있다.
항상 스프링 컨테이너 없이 테스트할 수 있는 방법을 가장 우선적으로 고려하자.
스프링의 설정을 이용한 DI 방식을 고려하는 경우
컨텍스트에서 DI받은 오브젝트에 다시 테스트 코드로 수동 DI해서 테스트하는 방식을 고려하는 경우
소스 코드 : github