인프런 커뮤니티 질문&답변

김선도님의 프로필 이미지

작성한 질문수

Practical Testing: 실용적인 테스트 가이드

서비스 계층의 테스트 범위에 대해서

작성

·

448

·

수정됨

5

//CommunityCommandService.java
public void updateCommunity(Long communityId, String description, List<String> newTags) {  
    Community community = communityRepository.findCommunityById(communityId);    
  
    community.updateCommunity(description, newTags);  
}

만약, 이런 코드가 있다고 하면

Service는 데이터를 받고 Community 클래스에게 실제 데이터 변경을 위임합니다.

 

//Community.java
public void updateCommunity(String description, List<String> tags) {
        this.description = new Description(description);
        this.hashtags.updateTags(tags, this);
}

 

Service 클래스를 테스트할 때

상태검증으로 테스트한다면,

//CommunityCommandService.java
@Test
void 상태검증_테스트() {
        community = new Community("dummy Intro", List.of("dummy tag"));
        given(communityRepository.findById(any)).willReturn(community);

        communityCommandService.updateCommunity("new intro", List.of("new tag"));

        assertThat(community.getIntroduce).isEqualTo("new intro");
        assertThat(community.getTags).containsExactly("new tag");
    }

이렇게 테스트하게 됩니다.

사실 데이터의 변경 자체는 CommunityTest에서 테스트 하였습니다.

//CommunityTest.java
   @Test
void updateCommunity() {
        final Community community = TestCommunity.builder().build();

        community.updateCommunity("커뮤니티 소개란 입니다.", List.of("태그1", "태그2"));

        assertThat(community.getHashtags()).extracting("tag").containsExactly("태그1", "태그2");
        assertThat(community.getDescription()).isEqualTo("커뮤니티 소개란 입니다.");
    }

 

이렇게 되면 CommunityCommandService에서 내부 Community.updateCommunity를 중복적으로 검증하는게 아닌지 생각이 듭니다.

객체지향 관점에서, Service는 Community에게 위임하여 상태를 변경시킵니다.

실제 내부에 어떠한 변화가 발생했는지에 집중하는게 아니라, 객체에게 위임했는지 여부에 좀 더 초점을 맞춰야하는 걸까요? (verify를 통해서)

 

질문

  1. 저는 사실 상태검증을 더 좋아합니다. 상태검증을 사용한다면 위와 같은 중복검증이 발생하게 되는걸까요? 객체지향의 관점에서 보면 사실 verify를 통해서 호출여부를 판단하는게 더 좋아보입니다. 다만, 상태검증을 위해서라면 위와 같이 중복적으로 검증하는 것을 피할 수 없는건가요?

  2. 강사님의 경우, 상태검증을 할 때 위와같이 Community 로직을 다른 곳에서 테스트하였다고 해도, Service에서 다시 테스트하나요?

답변 1

2

박우빈님의 프로필 이미지
박우빈
지식공유자

안녕하세요, 김선도님! :)
좋은 질문이네요 ㅎㅎ 답변 드리겠습니다.

1번과 2번 질문 모두 같은 맥락으로 답변 드릴 수 있을 것 같네요.
저도 한 때 많이 고민했던 질문인데요, 지금은 다음과 같이 결론을 내렸습니다.
테스트 내용의 중복이 발생하더라도 확실한 검증을 하는 방향이 안정적이다.

말씀해주신 상황은 Community의 단위 테스트에서 update 로직을 검증하는 상황이고, CommunityCommandService의 통합 테스트를 시도하면서 로직이 얇다 보니 단위 테스트와 큰 차이가 없기 때문에 드는 고민으로 보이는데요.
(서비스의 로직이 얇지 않더라도 같은 내용을 한번 더 검증해야 하는지 고민이 들 수도 있고요.)

한번 다음과 같은 상황을 고려해보면 좋겠습니다.

[중복 검증이라 생각해 테스트 코드를 작성하지 않는 경우]

  • 비즈니스 요구사항이 발전하여 서비스 로직의 복잡도가 증가하는 경우, 테스트 코드가 없다면?

    • (미래 시점에) 테스트 코드가 존재하는 상태에서 그 코드를 인지하고 보완하는 것과, 테스트 코드가 존재하지 않는 상태에서 다른 사람이 새로운 테스트 코드를 작성할 확률 중에 더 안정성 있는 방향은?

  • 단위 테스트로는 update 기능이 정상 동작함을 증명했으나, CommunityCommandService에서 JPA의 변경 감지 등이 모종의 이유로 동작하지 않았다면? (예를 들어 트랜잭션 처리가 빠져있었다던가)

    • 실제로는 쿼리가 날라가지 않고 해당 서비스의 updateCommunity()는 기대한 대로 동작하지 않았을 것이다.

[중복 검증이라 생각해 verify()로 검증하는 경우]

  • 서비스 테스트에서 해당 update를 verify()로 검증한 경우, 나중에 비즈니스 로직이 발전하여 해당 서비스에서 Community 상태를 여러 번 각기 다른 메서드로 상태를 변경하게 된다면?

    • 최종 상태값을 검증하지 않는다면 Community.updateCommunity()가 호출되었다고 하더라도 최종적으로는 원하는 상태값이 아닐 수도 있다.

'내가 작성한 코드가 객체지향을 고수하기 때문에 테스트 코드도 그 방식을 존중해야 한다'라는 방향 보다는, 위와 같은 미래 시점의 문제를 내포하고 있기 때문에 약간의 중복을 감수하고 확실한 검증을 꾀하는 것이 나아보입니다.

물론 예시로 들어주신 코드가 CommandService 인 것을 봐서는, CQRS가 적용되어 있는 코드인 것 같은데요ㅎㅎ
CommandService의 특성상 Command 행위에만 집중하기 때문에 해당 Service의 복잡도가 미래 시점에도 다른 Service보다 크게 복잡해지지는 않을 것 같지만(거의 그대로일 것이라 기대하지만), 그렇다고 하더라도 저는 동일한 이유로 상태 검증을 택하는 것이 낫다는 생각입니다.

도움이 되셨기를 바랍니다.

감사합니다. :)

김선도님의 프로필 이미지
김선도
질문자

항상 고민했던건데 어느정도 정리되었습니다.

좋은 답변 감사드립니다!!