레시피 16-7 통합 테스트에서 DB 액세스하기

umtuk·2022년 1월 28일
0

통합 테스트에서 DB 액세스하기

과제

DB에 접속하는 애플리케이션, 특히 ORM 프레임워크로 개발된 애플리케이션을 통합 테스트할 때 테스트 데이터를 미리 준비하고 테스트 메서드 실행 이후 데이터를 검증할 수 있게 DB에 직접 액세스하기

해결책

스프링 테스트 지원 기능을 이용하면 테스트에서 각종 DB 작업 시 JDBC 템플릿을 사용 가능
테스트 컨텍스트 프레임워크의 지원 클래스를 상속하면 미리 준비된 JdbcTemplate 인스턴스 사용 가능
데이터 소스와 트랜잭션 관리자는 빈 구성 파일에 선언해야 함

풀이

테스트 컨텍스트 프레임워크에서 지원되는 클래스를 상속하여 테스트 클래스를 작성하면 protected 필드로 JdbcTemplate 인스턴스 참조 가능
JUnit을 사용하면 AbstarctTransactionalJUnit4SpringContextTests 클래스를 상속
이 클래스는 테이블 로우 개수를 세거나 테이블에서 로우를 삭제하고 SQL 스크립트를 실행하는 등 정형화된 작업을 수행하는 편의성 메서드 제공

@ContextConfiguration(classes = BankConfiguration.class)
public class AccountServiceJUnit4ContextTests extends AbstractTransactionalJUnit4SpringContextTests {

    private static final String TEST_ACCOUNT_NO = "1234";

    @Autowired
    private AccountService accountService;

    @Before
    public void init() {
        executeSqlScript("classpath:/bank.sql",true);
        jdbcTemplate.update(
                "INSERT INTO ACCOUNT (ACCOUNT_NO, BALANCE) VALUES (?, ?)",
                TEST_ACCOUNT_NO, 100);
    }

    @Test
    public void deposit() {
        accountService.deposit(TEST_ACCOUNT_NO, 50);
        double balance = jdbcTemplate.queryForObject(
                "SELECT BALANCE FROM ACCOUNT WHERE ACCOUNT_NO = ?",
                Double.class, TEST_ACCOUNT_NO);
        assertEquals(balance, 150.0, 0);
    }

    @Test
    public void withDraw() {
        accountService.withdraw(TEST_ACCOUNT_NO, 50);
        double balance = jdbcTemplate.queryForObject(
                "SELECT BALANCE FROM ACCOUNT WHERE ACCOUNT_NO = ?",
                Double.class, TEST_ACCOUNT_NO);
        assertEquals(balance, 50.0, 0);
    }
}

executeSqlScript() 메서드 대신 클래스/메서드 레벨에 @Sql을 붙이면 원하는 SQL이나 스크립트 실행 가능

@ContextConfiguration(classes = BankConfiguration.class)
@Sql(scripts="classpath:/bank.sql")
public class AccountServiceJUnit4ContextTests extends AbstractTransactionalJUnit4SpringContextTests {

    private static final String TEST_ACCOUNT_NO = "1234";

    @Autowired
    private AccountService accountService;

    @Before
    public void init() {
        jdbcTemplate.update(
                "INSERT INTO ACCOUNT (ACCOUNT_NO, BALANCE) VALUES (?, ?)",
                TEST_ACCOUNT_NO, 100);
    }
    ...
}

@Sqlscripts 속성에 실행 스크립트 지정 가능
statements 속성에 SQL문을 직접 써넣을 수 있음
executionPhase 속성에서는 테스트 메서드 이전에 실행할지(ExecutionPhase.BEFORE_TEST_METHOD), 이후에 실행할지(ExecutionPhase.AFTER_TEST_METHOD) 설정 가능
클래스/메서드에 @Sql을 여러 개 붙여 테스트 전후에 특정 SQL문 실행 가능

TestNG에서는 AbstractTransactionalTestNGSpringContextTests를 상속해 JdbcTemplate 인스턴스를 가져옴

@ContextConfiguration(classes = BankConfiguration.class)
@Sql(scripts="classpath:/bank.sql")
public class AccountServiceTestNGContextTests extends AbstractTransactionalTestNGSpringContextTests {

    private static final String TEST_ACCOUNT_NO = "1234";

    @Autowired
    private AccountService accountService;

    @BeforeMethod
    public void init() {
        jdbcTemplate.update(
                "INSERT INTO ACCOUNT (ACCOUNT_NO, BALANCE) VALUES (?, ?)",
                TEST_ACCOUNT_NO, 100);
    }

    @Test
    public void deposit() {
        accountService.deposit(TEST_ACCOUNT_NO, 50);
        double balance = jdbcTemplate.queryForObject(
                "SELECT BALANCE FROM ACCOUNT WHERE ACCOUNT_NO = ?",
                Double.class, TEST_ACCOUNT_NO);
        assertEquals(balance, 150.0, 0);
    }

    @Test
    public void withDraw() {
        accountService.withdraw(TEST_ACCOUNT_NO, 50);
        double balance = jdbcTemplate.queryForObject(
                "SELECT BALANCE FROM ACCOUNT WHERE ACCOUNT_NO = ?",
                Double.class, TEST_ACCOUNT_NO);
        assertEquals(balance, 50.0, 0);
    }
}
profile
https://github.com/umtuk

0개의 댓글