테스트 코드를 작성해야 하는 이유

alsdl0629·2023년 8월 24일
0

테스트 코드

목록 보기
1/2
post-thumbnail

이번 글에서는 박우빈님의 "Practical Testing: 실용적인 테스트 가이드"를 보고 테스트 코드에 대한 제 생각을 정리해 보려고 합니다.


테스트 코드란?

내가 작성한 실제 코드가 제대로 동작하는지 검증하는 코드입니다.
테스트 코드와 관련된 TDD, BDD와 같은 개발 방법론이 존재합니다.

자바 진영에서는 JUnit 프레임워크를 사용해서 테스트 코드를 작성할 수 있습니다.

외부 기술을 공부할 때 테스트 코드를 사용하면 여러 가지 경우를 직접 설정해 구체적인 기능을 재밌게 학습할 수 있는 장점도 있습니다.


테스트 코드를 작성하지 않으면 발생하는 일

제가 현재 겪고 있는 일입니다.
저는 현재 팀 프로젝트를 진행하고 있습니다.
이 프로젝트에서는 테스트 코드를 전혀 작성하지 않고 서비스 코드 작성 후 postman을 사용해서 성공 사례와 간단한 예외 케이스만 확인한 후 바로 배포해서 프론트 분들께서 이어서 개발을 진행하십니다.

당시에는 테스트 코드의 중요성을 못 느껴서 작성하지 않고 진행했었는데 지금 와서 보니 오류가 언제 발생할지 모르는 프로젝트가 되었고 개발하는 시간보다 예상치 못한 곳에서 터지는 오류(예외 처리, 유효성 검증 등)를 고치는 시간이 더 많아졌습니다.

결국 테스트 코드가 없으면 내 코드가 의도한 대로 동작하는지 검증할 방법이 없어 경험과 감에 의존한 채 개발을 진행하게 되고 결국에는 불안정한 서비스가 될 것입니다.

그리고 오류를 배포 후에야 확인할 수 있어 서비스에 대한 피드백이 늦고 유지보수는 점점 어려워지고 서비스의 품질이 떨어질 것입니다.


테스트 코드를 작성해야 하는 이유

테스트 코드 == 문서화

@SpringBootTest // 서버를 띄워 통합 테스트를 진행
class ProductServiceTest {

    @Autowired
    private ProductService productService;

    @Autowired
    private ProductRepository productRepository;

    @AfterEach // 각 테스트가 끝난 후 실행
    void tearDown() {
        productRepository.deleteAllInBatch();
    }

    @DisplayName("신규 상품을 등록한다. 상품번호는 가장 최근 상품의 상품번호에서 1 증가한 값이다.")
    @Test
    void createProduct() {
        // given
        Product product = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000);
        productRepository.save(product);

        ProductCreateServiceRequest request = ProductCreateServiceRequest.builder()
                .type(HANDMADE)
                .sellingStatus(SELLING)
                .name("카푸치노")
                .price(5000)
                .build();

        // when
        ProductResponse productResponse = productService.createProduct(request);

        // then
        assertThat(productResponse)
                .extracting("productNumber", "type", "sellingStatus", "name", "price")
                .contains("002", HANDMADE, SELLING, "카푸치노", 5000);

        List<Product> products = productRepository.findAll();
        assertThat(products).hasSize(2)
                .extracting("productNumber", "type", "sellingStatus", "name", "price")
                .containsExactlyInAnyOrder(
                        tuple("001", HANDMADE, SELLING, "아메리카노", 4000),
                        tuple("002", HANDMADE, SELLING, "카푸치노", 5000)
                );

    }

    @DisplayName("상품이 하나도 없는 경우 신규 상품을 등록하면 상품번호는 001이다.")
    @Test
    void createProductWhenProductIsEmpty() {
        // given
        ProductCreateRequest request = ProductCreateRequest.builder()
                .type(HANDMADE)
                .sellingStatus(SELLING)
                .name("카푸치노")
                .price(5000)
                .build();

        // when
        ProductResponse productResponse = productService.createProduct(request.toServiceRequest());

        // then
        assertThat(productResponse)
                .extracting("productNumber", "type", "sellingStatus", "name", "price")
                .contains("001", HANDMADE, SELLING, "카푸치노", 5000);

        List<Product> products = productRepository.findAll();
        assertThat(products).hasSize(1)
                .extracting("productNumber", "type", "sellingStatus", "name", "price")
                .contains(
                        tuple("001", HANDMADE, SELLING, "카푸치노", 5000)
                );
    }

상품을 등록하는 실제 코드를 테스트하는 코드입니다.
BDD 방식(given-when-then)으로 작성했습니다.

상품을 등록하는 기능을 포함해서 전체적인 서비스를 출시하고 유지보수를 해야 한다는 가정을 해보겠습니다.
그렇게 되면 서비스 인수인계를 해야할 때가 올 것입니다.

서비스를 넘겨받은 개발자는 서비스 코드를 이해하는 시간이 필요한 데,
이때 테스트 코드가 도움을 줄 수 있습니다.

테스트 코드를 보면 당시 개발자가 어떤 흐름으로 서비스를 구성했는지 알 수 있습니다.

결국 테스트 코드가 있게 되면 서비스를 넘겨받은 개발자는 쉽게 서비스에 적응할 수 있을 것입니다.

그리고 서비스를 유지 보수하는 개발자도 서비스를 더 잘 이해하면서 코드를 작성할 수 있습니다.


빠른 피드백

테스트 코드가 없어 코드를 바로 배포하게 되면 예상치 못한 오류 (예외 처리를 하지 않았거나, 유효성 검증을 하지 않거나 등등 ...) 를 만나게 될 확률이 높습니다. 그렇게 되면 서비스 품질과 서비스에 대한 사용자의 신뢰도가 굉장히 낮아질 것입니다.

하지만 테스트 코드를 작성하면 실제 코드가 의도한 대로 동작하는지 바로 확인할 수 있어 예상치 못한 오류를 만날 확률이 낮습니다.


과감한 리팩토링


테스트 코드가 의도한 대로 동작하면 위 이미지처럼 초록 불이 뜰 것입니다.

테스트 코드를 제대로 작성했다는 가정하에,
실제 코드를 과감하게 리팩토링해도 초록 불만 확인하면 코드가 제대로 작동하는지 검증할 수 있습니다.

실제 코드 작성 -> 테스트 코드 작성 -> 초록 불 확인 -> 리팩토링 -> 초록 불 확인 ...
위 사이클을 반복하면서 서비스를 구축하고 리팩토링하면
리팩토링 시간을 단축하고 전보다 클린한 코드를 작성할 수 있을 것입니다.

안정적인 서비스

테스트 코드가 있으면 개발자는 언제든지 테스트 코드를 실행해서 실제 코드가 의도한 대로 동작하는지 확인할 수 있습니다.

유지보수하는 과정에서 코드가 수정되는 부분이 있을 수 있는데 이럴 때마다 테스트 코드를 실행해 실제 코드가 잘 작동하는 걸 확인하면 안정적인 서비스를 구축할 수 있을 것입니다.

또한 빠르게 변화하는 소프트웨어의 안정성을 테스트 코드를 통해 보장할 수 있습니다.


느낀점

일단 테스트 코드에 관해 공부하면서 전에 테스트 코드를 작성하지 않던 저 자신이 점점 미워졌습니다 😭😭😭

계층형 아키텍처에서 헥사고날 아키텍처 도입하기
당시에도 테스트 코드가 없어 수동으로 테스트를 진행해서 리팩토링하기가 굉장히 힘들었었습니다.

저는 개발자에게 중요한 것 중 하나는 자신이 하는 일에서 반복되는 일을 자동화해서 시간을 단축하고 실수를 줄이는 게 중요하다고 생각합니다.

테스트 코드는 제가 수동으로 테스트한 것을 이른 시간 안에 정확하게 자동으로 확인해 주기 때문에 좋은 서비스를 개발할 수 있도록 해주는 것 같습니다.

테스트 코드는 공부하면서 느끼는 거지만 정말 필수인 것 같습니다.

이 글을 읽고 있는 다른 개발자분들도 프로젝트에 테스트 코드를 작성해서 다양하고 좋은 경험을 해보시면 좋을 것 같습니다!

profile
인풋보다 아웃풋

0개의 댓글