본문 바로가기
Spring&SpringBoot/<토비의 스프링 3.1 Vol1.1>, 이일민

스프링을 사용한 테스트

by 민휘 2023. 3. 8.

스프링을 사용하는 테스트는 애플리케이션 컨텍스트가 동작하는 환경에서 빈이 잘 동작하는지를 확인하고 싶을 때 쓴다.

그게 의도가 아니라면 애플리케이션 컨텍스트와 독립적인 유닛 테스트를 작성한다.

애플리케이션 컨텍스트는 생성 비용이 많이 들기 때문에, 애플리케이션 컨텍스트는 생성하면 테스트에서 공유한다.

테스트 전용 설정을 사용하고 싶다면 수동 DI보다는 테스트 전용 설정 파일을 만든다.

 

테스트를 위한 애플리케이션 컨텍스트 관리

애플리케이션 컨텍스트가 만들어질 때는 모든 싱글톤 빈 오브젝트를 초기화한다.

애플리케이션 컨텍스트는 비용이 많이 드는 편이고, 생성 후에는 변경이 잘 발생하지 않는다.

이런 경우에는 테스트 전체가 공유하는 오브젝트를 만든다.

 

JUnit이 제공하는 @BeforeClass 스태틱 메소드를 사용할 수도 있다.

이보다는 스프링이 제공하는 테스트 컨텍스트 프레임워크를 사용하는 것이 더 편리하다.

간단한 애노테이션 설정으로 애플리케이션 컨텍스트를 모든 테스트를 공유한다.

동일한 설정정보를 사용하면 메소드 뿐만 아니라 클래스에서도 공유 가능하다.

 

스프링 테스트 애플리케이션 컨텍스트를 사용해 스프링 컨테이너를 활용한 테스트를 작성해보자.

  • @RunWith(SpringJUnit4ClassRunner.class) : JUnit용 테스트 컨텍스트 프레임워크 확장 클래스를 사용한다. JUnit이 테스트를 진행하는 중에 테스트가 사용할 애플리케이션 컨텍스트를 만들고 관리하는 작업을 한다.
  • @ContextConfiguration : 테스트 컨텍스트가 자동으로 만들어줄 애플리케이션 컨텍스트의 설정파일 위치를 지정한다.
  • @Autowired : 테스트 컨텍스트 프레임워크가 변수 타입과 일치하는 컨텍스트 내의 빈을 찾는다. 타입이 일치하는 빈이 둘 이상이면 변수 이름으로 찾는다. 애플리케이션 컨텍스트는 초기화할 때 자기 자신을 빈으로 등록하기 때문에 아래 코드에서 ApplicationContext를 주입받을 수 있다. 구체적인 설정 정보가 필요한 경우가 아니라면 추상화에 의존하는 것이 좋다.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class UserDaoTest {
    @Autowired
    private ApplicationContext context;

 

 

DI를 활용한 테스트 전략

테스트 코드에서는 운영용과는 다른 컨텍스트의 빈을 다르게 두어 빈을 주입받고자 한다.

세 가지 정도의 방법을 사용할 수 있다.

(1) 수동 DI, (2) 테스트용 애플리케이션 컨텍스트 별도 생성, (3) 애플리케이션 컨텍스트 없이 테스트를 실행하는 방법이다.

 

간단하게 비교해보자. 항상 스프링 컨테이너 없이 테스트할 수 있는 방법을 우선으로 고려한다. 가장 빠르고 간결하다.

만약 의존관계가 복잡한 오브젝트를 테스트할 때는 테스트 전용 설정파일 만들어 사용하는 것이 좋다. 이것이 가장 일반적이다.

수동 DI를 사용하는 것은 가급적 피하는게 좋다. 애플리케이션 컨텍스트를 다시 만들어야하기 때문이다. 하지만 예외적인 의존관계를 강제로 구성해야한다면 사용한다.

 

수동 DI

수동 DI를 사용할 때는 운영용 애플리케이션 컨텍스트로부터 주입받은 빈의 수정자를 사용하여 상태를 변경한다. 이때 이 테스트 클래스에는 @DirtiesContext를 추가하여 애플리케이션 컨텍스트를 공유하지 않고 새로 생성한다.

@DirtiesContext
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations="applicationContext.xml")
public class UserDaoTest {
    @Autowired
    private ApplicationContext context;

    @BeforeEach
    public void setUp() {
        DataSource dataSource = new SingleConnectionDataSource("jdbc:mysql://localhost/toby", "root", "", true);
        dao.setDataSource(dataSource);
				// ...

 

테스트 전용 설정파일

테스트 전용 설정파일을 만들고 테스트에서는 항상 테스트 전용 설정파일만 사용하게 해주면 된다.

@DirtiesContext
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations="test-applicationContext.xml")
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="connectionMaker" class="springbook.user.dao.NConnectionMaker"/>

    <bean id="userDao" class="springbook.user.dao.UserDao">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/testdb?characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value=""/>
    </bean>

</beans>

 

스프링 컨테이너 없는 테스트

가장 권장되는 방법은 컨테이너 없는 DI 테스트이다. 테스트의 관심사는 오브젝트가 의도한 일을 정확하게 수행하는지이지 스프링 컨테이너에서 잘 동작하는지가 아니다. 따라서 테스트에 필요한 빈은 생성자로 직접 생성해서 사용해도 된다. 애플리케이션 컨텍스트에 의존하지 않으므로 실행에 더 빠르고 코드가 더 단순해진다. DI를 위해 반드시 컨테이너가 필요한 것은 아니다. 코드로도 충분히 구현할 수 있다. DI는 객체지향 프로그래밍 스타일이기 때문이다.

@BeforeEach
public void setUp() {
	this.user1 = new User("minpearl", "민휘", "lololo");
	this.user2 = new User("seeun", "세은", "030614");
	this.user3 = new User("kong", "콩이", "piggy");

	this.dao = new UserDao();
	DataSource dataSource = new SingleConnectionDataSource("jdbc:mysql://localhost/toby", "root", "star0826", true);
	dao.setDataSource(dataSource);
}