Soft Delete 진짜 잘 되고있는거 맞아?(Local DB에서 테스트하기)

Sol's·2023년 1월 10일
0

오류

목록 보기
10/18

@DataJpaTest를 통한 Test

Soft Delete기능을 만들고 Test코드까지 만들어 확인을 하였다.

@DataJpaTest를 통해 Test를 진행하였다.
DB는 h2DB를 사용했다.

server:
  servlet:
    encoding:
      force-response: true
spring:
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1
    username: sa
    password:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
  h2:
    console:
      enabled: true

logging:
  level:
    org:
      hibernate:
        SQL: DEBUG
@DataJpaTest
class PostRepositoryTest {

    @Autowired
    PostRepository postRepository;
    @Autowired
    UserRepository userRepository;

    @Test
    @DisplayName("soft delete 테스트")
    void post_softDelete_success() {
        //Post에 적용할 user를 생성
        User user = User.builder()
                .userName("userName")
                .password("password")
                .role(UserRole.ROLE_USER)
                .build();
        //user 저장
        User saveUser = userRepository.save(user);
        //Post 생성
        Post post = Post.builder()
                .title("test")
                .body("softDeleteTest")
                .user(saveUser)
                .build();
        //글쓰기
        Post savePost = postRepository.save(post);

        //게시글 확인
        assertEquals(postRepository.count(),1);
        assertEquals(savePost.isSoftDeleted(),false);

        //게시글 삭제
        savePost.deleteSoftly(LocalDateTime.now());

        //게시글 삭제 확인
        assertEquals(postRepository.count(),0);
        assertEquals(savePost.isSoftDeleted(),true);
    }
}
  • Test 성공!

문뜩 생긴 의문

그런데 의문이 생겼다.
진짜 잘 되는거 맞아??

Soft Delete라면 물리적 삭제가 아닌 논리적 삭제가 되어야 하니까, 실제 DB의 값은 존재해야한다.

하지만 위의 로직으로는 DB의 값이 존재하는지 안하는지 확인 할 수가 없다.

Post에 @Where조건으로 쿼리조회시 deltedAt = null일경우만 조회되게 설정했기 때문이다.

그렇기 때문에 게시글을 삭제하고 DB를 Conunt했을시 0이 나온것이다.

원하는것은 논리적 삭제가 구현되었는지 DB에서 직접 확인하고 싶었다.

Test로 DB를 조회하는 방법은 없을까??

통합테스트, 그리고 @ActiveProfiles

DB를 직접 확인하기위해 Local DB를 이용할 것이다.
@SpringBootTest를 통해 통합테스트를 진행하고
@ActiveProfiles("test")로 application-test.yml파일을 적용시켰다.
DB password는 환경변수로 주입시켰다.

server:
 servlet:
   encoding:
     force-response: true
spring:
 jpa:
   hibernate:
     ddl-auto: create
   show-sql: true
 datasource:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://localhost:3306/test
   username: root
   password: 
 mvc:
   pathmatch:
     matching-strategy: ant_path_matcher

자 이제 세팅은 끝났다 Test를 진행해 보자.

Test에서의 @Transactional

한번에 되는것은 없다.

@SpringBootTest
@ActiveProfiles("test") // local db에 row가 있는지 직접 확인 하고 싶을 때 쓴다
class PostServiceWithSpringTest {

    @Autowired
    PostRepository postRepository;
    @Autowired
    UserRepository userRepository;

    User user;
    Post post;

    @BeforeEach
    void setUp() {
        user = User.builder()
                .userName("userName")
                .password("password")
                .role(UserRole.ROLE_USER)
                .build();
        post = Post.builder()
                .title("test")
                .body("softDeleteTest")
                .user(null)
                .build();
    }

    @Test
    @DisplayName("soft delete 테스트")
    void post_softDelete_success() {
        //user 저장
        User saveUser = userRepository.save(user);
        //Post 생성
        post.setUser(saveUser);
        //글쓰기
        Post savePost = postRepository.save(post);

        //게시글 확인
        assertEquals(postRepository.count(),1);
        assertEquals(savePost.isSoftDeleted(),false);
        assertNull(savePost.getDeletedAt());

        //게시글 삭제
        savePost.deleteSoftly(LocalDateTime.now());

        //게시글 삭제 확인
        assertEquals(postRepository.count(),0);
        assertEquals(savePost.isSoftDeleted(),true);
        assertNotNull(savePost.getDeletedAt()); //삭제가 되었다는 뜻
    }
}

위와같이 Test를 작성하고 실행해 보았다.

그런데 테스트가 통과하지 못했다.
원인은 soft delete가 실행되지 않았던 것이다...

왜그런걸까? 생각해보니 @Transactional 어노테이션을 빼먹어서 그런것을 깨달았다!

그래서 바로 적용시켜보았다.

@SpringBootTest
@Transactional //Test에 Transactional을 붙이면 끝나면 롤백된다.?
@ActiveProfiles("test") // local db에 row가 있는지 직접 확인 하고 싶을 때 쓴다
class PostServiceWithSpringTest {

테스트가 통과되었다.

그런데 왜 DB에는 적용이 안된걸까??
Soft delete라면 post는 존재하지만 논리적으로만 삭제되어야 할텐데??

@RollBack

Test는 @RollBack 어노테이션이 따로 존재한다.
일반적으로 Test code는 DB에 반영하지 않고 로직 진행후
다시 rollback 하는 것이 true 로 되어있는대 이게 싫다면
@Rollback(value false) 옵션 적용하면 된다!

@SpringBootTest
@Transactional //Test에 Transactional을 붙이면 끝나면 롤백된다.?
@Rollback(value = false)
@ActiveProfiles("test") // local db에 row가 있는지 직접 확인 하고 싶을 때 쓴다
@Slf4j
class PostServiceWithSpringTest {

    @Autowired
    PostRepository postRepository;
    @Autowired
    UserRepository userRepository;

    User user;
    Post post;
    Like likes;

    @BeforeEach
    void setUp() {
//        postRepository.deleteAll();
//        userRepository.deleteAll();
        user = User.builder()
                .userName("userName")
                .password("password")
                .role(UserRole.ROLE_USER)
                .build();
        post = Post.builder()
                .title("test")
                .body("softDeleteTest")
                .user(null)
                .build();
    }

    @Test
    @DisplayName("soft delete 테스트")
    void post_softDelete_success() {
        //user 저장
        User saveUser = userRepository.save(user);
        //Post 생성
        post.setUser(saveUser);
        //글쓰기
        Post savePost = postRepository.save(post);

        //게시글 확인
        log.info("\n" + "count" + "\n");
        assertEquals(postRepository.count(),1);
        assertEquals(savePost.isSoftDeleted(),false);
        assertNull(savePost.getDeletedAt());

        //게시글 삭제
        log.info("\n" + "deleteSoftly" + "\n");
        savePost.deleteSoftly(LocalDateTime.now());
//        postRepository.save(savePost);

        //게시글 삭제 확인
        log.info("\n" + "count 2" + "\n");
        assertEquals(postRepository.count(),0);
        assertEquals(savePost.isSoftDeleted(),true);
        assertNotNull(savePost.getDeletedAt()); //삭제가 되었다는 뜻
    }
}

정리

Soft Delete의 테스트방식을 의심하는것부터 시작해서
Soft Delete를 LocalDB에 적용해보고 실제로 확인하는 작업을 하였다.
그 과정에서 LocalDB에 Test를 연결하는 방법을 알았고,
@Transactional 어노테이션의 동작방식에 대해서 조금은 더 익숙해진것 같다.

하지만 아직 완벽히 이해한것은 아니여서 엔티티 매니저, 영속성컨택스트, 1차캐시, 더티체킹, flush commit
JPARepository에서 제공하는 메서드 동작방식위주로 더 공부를 해봐야겠다.

해결하는데 시간은 좀 걸렸지만, 많이 배울 수 있는 시간이였다!

참고자료

@SpringBootTest / @DataJpaTest 차이점 과 JPA 영속성 컨텍스트

profile
배우고, 생각하고, 행동해라

0개의 댓글