본문으로 건너뛰기

JUnit5 테스트 여러가지 삽질기..

유닛 테스트로만 할것인지 (단순 기능 / 단순 메서드) 혹은 어떠한 서비스를 통으로 할것인지 에 따라서 테스트 코드 작성시 코드가 많이 달라진다

단순 메서드 / 유닛테스트

이 경우에는 딱히 어려움이 없을것으로 판다.. 원하는데로 테스트코드를 작성해도 되고, 테스트 코드에 메서드를 통으로 작성해도 상관없다.

스프링의 DI 를 사용하면서 실제 디비 엑세스를 하고 싶다(Dao사용)

이 경우에는 특정 서비스를 통으로 테스트 하고 싶을때 발생하는데 @Autowired를 사용해야 하므로 DI 를 사용하게 되며, 스프링이 의존성 주입을 자동으로 하게 해야 하는데(스프링을 써야한다는 소리) @SpringBootTest 어노테이션이나 @RunWith(SpringRunner.class)을 적절하게 사용하여야 한다.
또한, 테스트 사용시의 DataSource를 따로 설정해줘야 한다
보통 src -> main -> resources -> application.yml 이 있고 그곳에 디비컨넥션 설정을 하게될 것이다.
마찬가지로.. 테스트 코드가 Dao를 통해서 DB에 엑세스 하려고 한다면 application.yml을 읽어들여서 DB접속을 하게되는데
테스트 코드는 test -> resources -> application.yml 을 읽어들려서 디비에 접속을 하려고 하므로, 테스트용 application.yml을 적절하게 만들어 둬야 한다.
원래있던 application.yml 을 복붙하덩가. 적절하게 수정(더욱 간단하게)하여 사용하면된다.

Mock테스트는 어떨때 사용하면 좋을까?

위에와 같이 하나의 서비스를 통으로 테스트 할때 실제 DB에 엑세스 하는 이유는 그만큼 서비스 내에 Dao가 많고 / 많은 데이터를 읽어 오기때문에 그런것인데
만약 서비스를 통으로 하더라도, 읽어오는 데이터 자체가 적거나(List오브젝트인데 그 수가 적거나, 아예 단일 오브젝트인경우) Dao가 거의 없을 경우에는 Mock을 사용하는것이 더 나을수도 있다.
또한, Mock을 사용하기 좋을 타이밍은. 비지니스 로직 설계부분이 얼추 완성되어있는데 데이터 엑세스부분이(어느 테이블에서 어느데이터를 뽑아와야 하는지 부정확할때) 아직 미정일때 사용하면 딱 좋다

Mock을 아무때나 막 사용하는것이 아니라. Mock으로 유리한 상황에는 Mock을, 직접 DB 엑세스를 해야할 때는 직접 DB엑세스를 하는것이 낫다는 결론

DBunit에 대한 오해..

DbUnit은 Flyway 처럼 스키마 생성을 해주거나 테이블을 정의 해주는것이 아니다. 또한, 물리적 DB가 없는데 가상으로?? 만들어주는것도 아니다.
DbUnit은 물리적인 DB가 존재하고. 스키마 생성이 되어있는 상태(테이블 정의가 끝나있는 상태:CREATE문 등등..)에서 단순하게 테이블에 지정한대로 데이터를 집어넣거나 삭제해줄 뿐이다. 그러므로 DbUnit을 사용하려면 Docker같은것으로 DB를 띄워놓거나, 혹은 DataSource에서 지정한 디비쪽에 접속이 가능한 상태여야하며. flyway로 스키마 생성과 테이블정의가 다 되어있어야 한다. 혹은 이미 돌아가고있는 DB이여야 한다(이미 스미카생성 테이블정의 다 되고 계속 돌아가고있는 DB)
XML형식으로 테이블에 넣을 데이터를 작성할 수 있는데 ID를 꼭 넣어야한다. XML형식으로 데이터를 만들어둘때 ID 지정을 안하면. INSERT는 문제없지만 테스트 끝나고 DELETE할때 pk로 특정할 수 없다고 삭제 불가능하다고 에러가 발생한다
DbUnitTestConfig클래스에 @Configuration어노테이션을 붙혀서 DataSource나 컨넥션 설정을 @Bean으로 주입한다. 그리고 이것을 테스트 클래스에서 import해서 사용한다

테스트코드의 클래스들이 있는 곳에 적절히 패키지를 만들고(testconfig) 이러한 클래스를 만들어둔다

@Configuration
public class DbUnitTestConfig {

@Bean
public DatabaseConfigBean dbUnitDatabaseConfig() {
DatabaseConfigBean config = new DatabaseConfigBean();
config.setAllowEmptyFields(true);
config.setDatatypeFactory(new MySqlDataTypeFactory());
config.setMetadataHandler(new MySqlMetadataHandler());
return config;
}

@Bean
public DatabaseDataSourceConnectionFactoryBean dbUnitDatabaseConnection(DataSource dataSource) {
DatabaseDataSourceConnectionFactoryBean dbUnitDatabaseConnection = new DatabaseDataSourceConnectionFactoryBean();
dbUnitDatabaseConnection.setDataSource(dataSource);
dbUnitDatabaseConnection.setDatabaseConfig(dbUnitDatabaseConfig());
// dbUnitDatabaseConnection.setSchema("dbunit");
return dbUnitDatabaseConnection;
}
}

그리고 실제 사용할 때는 위의 클래스를 @Import하여 사용한다.

@DatabaseSetup(value = classpath:com/home/practicebatch/service/sampleData.xml 부분의 경로로 골치아팠었는데 src/test/resources/com/home/practicebatch/service/sampleData.xml 파일을 로드할때 위에와 같이 해줬다.
포인트는 classpath: 이닷!~
아마도 내 기억으로 classpath를 안써주면, 현재 시스템 기준으로 절대경로를 인식하고, classpath를 사용하게되면 현재 프로젝트 기준으로 절대경로를 인식할 것이다

@Import(DbUnitTestConfig.class)
@SpringBootTest
@DbUnitConfiguration(databaseConnection = "dbUnitDatabaseConnection")
//@DatabaseSetup(value = {"sampleData.xml"}, type = DatabaseOperation.INSERT)
//@DatabaseTearDown(value = {"sampleData.xml"}, type = DatabaseOperation.DELETE_ALL)
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DbUnitTestExecutionListener.class })

class PersonServiceTest {

@Autowired
private PersonService personService;

@Test
@DatabaseSetup(value = {"classpath:com/home/practicebatch/service/sampleData.xml"}, type = DatabaseOperation.INSERT)
@DatabaseTearDown(value = {"classpath:com/home/practicebatch/service/sampleData.xml"}, type = DatabaseOperation.DELETE)
public void testFindPerson() throws Exception {

List<Person> personList = personService.getPersonList("abcd");

assertEquals(1, personList.size());
assertEquals("abcd", personList.get(0).getFirstName());
}

}

DatabaseOperation

  • CLEAN_INSERT 하면 테이블 내용 다 지우고 INSERT하게 됨
  • DELETE_ALL 하면 테스트 끝날때 테이블 모든 내용 다 지우게됨
  • INSERT 기존 테이블 데이터는 내비두고 xml내용만 insert함
  • DELETE 테스트 끝나고 종료할때 insert했던것만 지움