현재노트

Respository나 Service단 로직을 검증하는 통합테스트 본문

Back/testCode

Respository나 Service단 로직을 검증하는 통합테스트

현재노트 2023. 6. 9. 16:07

통합 테스트는 Respository나 Service단 로직을 검증하는 테스트입니다.

 

 

springboot 기준으로 repository를 테스트할 때 @SpringBootTest 를 사용해도 되지만, Bean이 너무 많이 불러와져서 실행속도를 저하시키는 단점이 있습니다.

 

그래서 @DataJpaTest 를 사용하여 필요한 Bean만 불러들려 사용합니다.

 

테스트할 repository를 @Autowire로 빈을 가져오고 사용을 합니다.

 

서비스단을 테스트하는 방법은 mockito로 가짜 객체를 생성하는 방법과 h2 DB를 사용할경우에는 이것을 그대로 사용하는 방법이 있습니다.

 
 

 

mockito로 작업하는경우 실제 DB를 사용하는것이 아닌 Fake 객체를 이용하여 반환한 값을 검증하는 작업입니다.

 

 

위쪽에 @ExtendWith 설정을 해주고, 모킹을 할 Repository나 Service에 @Mock 를 사용하게 되면, 테스트할 준비가 됩니다

 

 

테스트 시작시, return 할 값들을 할당해주면, 할당된 값이 mockitong한 메소드 반환값으로 나오게됩니다.

 
 

 

Mocking이 장점은 DB를 사용하지 않기 떄문에 빠르다라는 장점이 있는반면, 외래키와 같은 외부적인 요소들은 검증할 수 없다는 단점이 있습니다.

 

두번쨰 통합테스트 방법은 @SpringBootTest 를 사용하여 하는것입니다.

 

 

 

이번에 방법은 Spring를 시작할떄와 같은 환경으로 테스트를 하는것이고, 실제 DB를 사용하는것이므로 테스트 환경이나 개발환경에서만 진행해주세요.

 

 

자신의 비지니스 로직이 제대로 작동했는지에 대해 알 수 있는 테스트이며, 실제환경과 거의 동일하게 작동한다는 장점이 있는 반면, 여러개의 통합테스트를 할떄 느려질 수 있다는 단점이 있어 테스트가 많아질경우에는 속도의 주범이 됩니다.

 
 

 

주의사항

 

통합 테스트를 진행하다보면 한 클래스를 진행할떄 다른 메소드에도 영향을 줄 수 있는 상황이 발생한다.

 

 

 

이럴경우에는 데이터를 클리어 해주는 방법을 많이 사용한다.

 

 

@Order 로 순서 정하기

 

 

 

테스트하는 순서를 정하여서 예상대로 결과가 나오게 진행한다.

 

하지만, 여기서 문제점이 있는데 만약 테스트코드를 짠 개발자 말고 다음 유지보수 받은 개발자가 있을경우, 순서에 대해 모르기 떄문에 테스트코드 짜기 어려움이 있게 된다.

 

 

 

2. @DirtesContext 사용

 

추천해주고 싶지 않은 방법이다.

테스트마다 Spring을 새로 띄우는 방식이여서 테스트마다 독립성을 보장되지만, 테스트 코드가 늘어날수록 속도가 엄청나게 느려진다.

 

 

3. deleteAll 시키기

 

 

테스트 할때 @BeforeEach나 @AfterEach 에서 DB 데이터를 제거하는 일이다.

 

 

현업에서 제일 많이 사용하는 방법이다.

데이터를 초기화 시키는데 비교적 짧은 코드로 되어 효과적이다.

 

 

 

4. @SQL 사용하기

 

delete 보다는 truncate로 지우는 방식이 훨씬 빠르기 떄문에 직접 SQL에 적어 사용하는 방법이다.

 

 

truncate table station

 

위처럼 작성되어 있는 sql문을 실행시켜 사용한다.

위에 deleteAll보다는 빠른 테스트가 가능하지만, 이것도 table이 늘면 늘수록 개발자가 직접 적어줘야하는경우가 많이 생긴다.

 

 

5. EntityManger 사용하기

 

@Service
public class DatabaseCleanup implements InitializingBean {

    @PersistenceContext
    private EntityManager entityManager;
    private List<String> tableNameList;

    @Override
    public void afterPropertiesSet() throws Exception {
        tableNameList = entityManager.getMetamodel()
                .getEntities()
                .stream()
                .filter(e -> e.getJavaType().getAnnotation(Entity.class) != null)
                .map(e -> CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, e.getName()))
                .collect(Collectors.toList());
    }

    @Transactional
    public void execute() {
        entityManager.flush();
        entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY FALSE").executeUpdate();

        for (String tableName : tableNameList) {
            entityManager.createNativeQuery("TRUNCATE TABLE " + tableName).executeUpdate();
            entityManager.createNativeQuery("ALTER TABLE " + tableName + " ALTER COLUMN ID RESTART WITH 1").executeUpdate();
        }

        entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY TRUE").executeUpdate();
    }

 

테스트 코드 작성할때 같이 DatabaseCleanup 클래스를 사용해주는 것이다.

모든 Entity를 조회한후, truncate 로 초기화 해주는 방법이여서 한번 작성해두면 Entity가 늘어나도 추가적으로 작성할 로직은 없어지고 테스트를 독립적으로 작성가능하다.

 
Comments