ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 테스트 코드(Test Code)
    Background 2024. 2. 28. 18:47

    개발에서 테스트 코드의 작성이유와 가이드라인, 여러 개념들에 대해 정리하였습니다.

     

     


    < 테스트 코드란 >

    테스트 코드란 소프트웨어 개발 과정에서 주로 사용되는 코드 조각이나 전체 프로그램이 의도한 대로 정확하게 작동하는지 검증하기 위해 작성된 코드입니다.

     

    Ex) 두 숫자를 받아서 합을 반환하는  add  함수입니다.(with JEST)

    // add.js
    function add(a, b) {
      return a + b;
    }
    
    module.exports = add;
    // add.test.js
    const add = require('./add');
    
    test('adds 1 + 2 to equal 3', () => {
      expect(add(1, 2)).toBe(3);
    });

     


    테스트 주도 개발(Test Driven Development a.k.a. TDD)
    : 먼저 테스트를 작성하고 이를 통과하는 코드를 개발하는 접근법.

     

     


    < 테스트 코드의 중요성 및 작성이유 >

    1. 버그 조기 발견(디버깅 비용 감소)

    테스트 코드를 통해 개발 과정에서 버그를 조기에 발견하면 수정하는 데 필요한 비용이 크게 줄어듭니다. 테스트 코드를 작성함으로써 새로운 기능 추가나 기존 기능 수정 시에도 코드의 의도대로 작동하는지 확인할 수 있어, 잠재적인 버그를 미리 찾아내고 수정할 수 있습니다. 이는 개발 프로세스를 더욱 효율적으로 만들어줍니다.

     

     

    2. 개발 속도 향상

    테스트 코드를 작성하고 실행함으로써 개발자는 수동으로 테스트를 수행하는 시간을 절약할 수 있습니다. 자동화된 테스트를 통해 빠르게 코드 변경 사항을 확인하고 문제를 신속하게 파악할 수 있습니다.

     

     

    3. 리팩토링 용이성

    안정적인 테스트 코드가 있으면 기존 코드를 자유롭게 리팩토링(코드 구조를 개선)할 수 있습니다. 리팩토링 후에도 테스트를 통과한다면 기능이 예전과 동일하게 작동한다는 확신을 얻을 수 있습니다.

     

    Ex) 기존 함수 로직 변경

    // 기존 함수
    function add(a, b) {
      return a + b;
    }
    // 테스트 코드
    test('adds two numbers', () => {
      expect(add(1, 2)).toBe(3);
    });
    // 변경된 함수
    function add(a, b) {
      return a + b + 1; // 1을 추가로 더해줌
    }

    : 이 변경 사항이후 테스트 코드를 실행하면, 기존의 기능이 올바르게 작동하는지 확인할 수 있습니다. 만약 테스트가 실패한다면, 리팩토링한 코드에 문제가 있음을 알 수 있으며 이를 수정할 수 있습니다.

    이렇게 테스트 코드를 작성하면 코드 변경에 대한 안정성을 보장하고, 리팩토링 과정을 보다 안전하고 효율적으로 만듭니다.

     

     

    4. 문서화의 역할

    테스트 코드는 해당 함수나 모듈이 어떻게 사용되어야 하는지에 대한 실질적인 예시를 제공합니다. 즉, 테스트 코드 자체가 문서화의 역할을 하여, 새로운 개발자가 코드를 이해하기 쉽게 만듭니다.

     

    Ex) 문서화

    // greetUser.js
    function greetUser(name) {
      return `Welcome, ${name}!`;
    }
    
    module.exports = greetUser;
    // greetUser.test.js
    const greetUser = require('./greetUser');
    
    test('greets user with name', () => {
      expect(greetUser('Alice')).toBe('Welcome, Alice!');
    });

    : 위의 예시에서  greetUser  함수는 사용자 이름을 받아 환영 메시지를 반환하는 기능을 수행합니다. 테스트 코드는 이 함수가 올바르게 동작하는지 확인하며, 함수의 사용 방법과 기대되는 결과를 명확하게 보여줍니다. 새로운 개발자가 코드를 이해하고 사용하는 데 도움이 됩니다.

     

     


     

    < 테스트 코드 작성의 단점 >

    1.  오버 엔지니어링  : 과도한 테스트 코드 작성으로 인해 개발 및 유지 보수 과정이 복잡해지고 비효율적으로 이어질 수 있는 상황을 의미합니다.
    2.  학습 곡선 요구  : 새로운 개발자나 경험이 적은 개발자에게는 테스트 주도 개발(Test-Driven Development, TDD)이나 테스트 코드 작성에 대한 개념을 익히는 것이 다소 어려울 수 있습니다.

     

     


    < 테스트 코드 유형 >

    단위 테스트 (Unit Test) : 단위 테스트는 개별적인 코드 단위 즉 함수 및 메소드 또는 클래스의 특정 기능을 테스트하는 것입니다.

    • - 코드의 작은 부분을 격리된 환경에서 테스트하여 각 부분이 예상대로 작동하는지를 확인합니다.
    • - 주로 모의 객체(Mocking)를 사용하여 외부 의존성을 제어하고 테스트를 격리합니다.
    • - 소프트웨어의 각 부분이 정확하게 동작하는지 확인하는 데 사용됩니다.

     

    통합 테스트 (Integration Test) : 통합 테스트는 다수의 단위나 모듈이 상호 작용하는 과정을 테스트하는 것입니다.

    • - 단위 테스트보다는 범위가 크고, 여러 컴포넌트 간의 상호 작용을 확인합니다.
    • - 실제 데이터베이스, 외부 API 등과 같은 외부 의존성을 포함할 수 있습니다.
    • - 시스템의 여러 부분이 함께 작동하는지를 확인하는 데 사용됩니다.

     

    기능 테스트 (Functional Test 또는 End-to-End Test) : 기능 테스트는 소프트웨어의 기능이 완전히 동작하는지를 확인하는 테스트입니다.

    • - 시스템 전체적인 흐름을 테스트하며, 사용자의 관점에서 시스템이 예상대로 작동하는지 확인합니다.
    • - 주로 사용자 인터페이스(UI)를 테스트하고, 실제 사용자의 사용 시나리오를 시뮬레이션합니다.
    • - 소프트웨어의 완전한 기능을 검증하는 데 사용됩니다.

     

     


    < 테스트 커버리지 >

    테스트 커버리지(Test Coverage)란 소프트웨어의 테스트가 얼마나 많은 코드를 실행했는지를 나타내는 측정 지표입니다. 즉, 소프트웨어의 테스트 커버리지는 코드 베이스에서 실행된 코드의 비율을 나타냅니다. 이는 소프트웨어의 품질과 안정성을 평가하는 데 중요한 지표 중 하나입니다.

    테스트 커버리지를 통해 개발자는 어느 부분이 테스트되지 않았는지를 식별하고, 코드의 품질을 향상시키고 버그를 줄이기 위해 추가적인 테스트를 수행할 수 있습니다. 그러나 테스트 커버리지가 100%라고 해도 모든 버그를 찾을 수 있는 것은 아니며, 다양한 테스트 시나리오를 고려하여 테스트를 설계하는 것이 중요합니다.

     

    1. 라인 커버리지(Line Coverage): 소스 코드의 각 라인이 테스트 스위트에서 실행된 횟수를 나타냅니다. 라인 커버리지는 각 라인이 실행되었는지 여부를 확인하기 때문에 가장 기본적인 커버리지 유형 중 하나입니다.

    2. 브랜치 커버리지(Branch Coverage): 코드의 각 분기점(조건문, 분기문 등)이 테스트에서 실행되었는지 여부를 나타냅니다. 이는 코드의 모든 분기 조건이 테스트되었는지 여부를 확인하는 데 사용됩니다.

    3. 펑션/메서드 커버리지(Function/Method Coverage): 각 함수 또는 메서드가 테스트에서 호출되었는지 여부를 나타냅니다. 이를 통해 각 함수 또는 메서드가 코드 베이스의 일부로서 테스트되었는지를 확인할 수 있습니다.

     

     


    < Example >

    일반 코드(두 숫자를 더하는 함수) :

    function add(a, b) {
      return a + b;
    }

     

    테스트 코드( Jest 를 사용한 단위 테스트):

    // 테스트 코드: add 함수를 테스트하는 코드
    const { add } = require('./addition');
    
    // 단위 테스트: add 함수가 두 숫자를 제대로 더하는지 확인
    test('adds two numbers', () => {
      // Arrange: 테스트 데이터 및 환경 설정
      const a = 5;
      const b = 7;
    
      // Act: 테스트 대상 함수 실행
      const result = add(a, b);
    
      // Assert: 예상 결과와 실제 결과 비교
      expect(result).toBe(12);
    });
    
    // 추가적인 테스트 케이스 작성 가능
    test('adds two negative numbers', () => {
      const a = -3;
      const b = -4;
      expect(add(a, b)).toBe(-7);
    });
    
    test('adds zero to a number', () => {
      const a = 8;
      expect(add(a, 0)).toBe(8);
    });
    
    // 오류 케이스에 대한 테스트도 작성 가능
    test('adds non-number arguments', () => {
      const a = 'hello';
      const b = 'world';
      expect(() => add(a, b)).toThrow();
    });

     

     

     

     

    'Background' 카테고리의 다른 글

    Node.js란?  (0) 2022.05.14
    let, var, const의 차이점 & 호이스팅(Hoisting)  (0) 2022.04.29
    HTTPS  (0) 2022.03.14
    Project Workflow  (1) 2022.02.22
    HTTP  (0) 2022.02.10

    댓글

Designed by Tistory.