기타

테스트 코드 작성에 대한 고찰

jjin502 2023. 7. 23. 21:36

출처 : 소프트웨어공학포털

 

개요

요즘 프로젝트를 하면서, 주변사람들에게 이런 말을 듣고는 했다. "그런데, 테스트 코드는 작성 안 하세요?" 혹은 "TDD는 해?" 와 같은 말들. 나는 그동안 프로젝트를 완성하는 것에 초점을 맞추다 보니, 테스트 코드를 크게 염두에 두진 않았다. 그래서 지금 부터라도 소프트웨어 테스트에 대해 알아보고 추후에 프로젝트를 진행할 때 작성해보려고 한다.

 

소프트웨어 테스트란?

소프트웨어 테스트(software testing)는 주요 이해관계자들에게 시험 대상 제품 또는 서비스의 품질에 관한 정보를 제공하는 조사 과정이다. - 위키백과

 

즉, 소프트웨어가 의도한대로 동작한대로 동작하는지 테스트하는 행위를 일컫는다.

 

소프트웨어 테스트의 장점

옛날에는 사람들이 직접 테스트를 하고 또 테스트를 하는 팀이 별도로 있었지만, 지금은 개발자가 자동으로 테스트를 할 수 있는 환경이 조성되어있다고한다. 그래서 컴퓨터를 통해 자동으로 테스트함으로써 빠르고, 일관성 있는 테스트를 할 수 있다.

 

무엇보다도, "피드백은 빠른 주기로 개발 중에 받을 수 있다."는 점이다. 

개발자가 테스트 코드를 작성하고, 수시로 정상 동작하는지 확인이 가능하면서 거의 실시간으로 피드백을 받고 수정해나갈 수 있다. 이는 테스트 코드 안에서 개발자가 작성한 코드가 제대로 동작한다는 확신을 주기도 하고, 생산성을 높일 수 있다.

 

소프트웨어 테스트의 종류

본인이 작성한 코드를 테스트 하고 싶은 범위, 복잡성에 따라 3종류로 나눌 수 있다. 

 

Unit Test(유닛 테스트)

가장 작은 범위의 테스트로 크게 복잡하지 않다. 개별 함수나 메서드, 클래스 혹은 컴포넌트의 동작을 테스트 한다. 가장 적은 비용이 들기도하고, 간단하다는 이유로 가장 빈번하게 수행할 수 있는 테스트이다.

 

Integration Test(통합 테스트)

통함테스트는 두 개 이상의 모듈이 결합해서 잘 동작하는지를 확인하고 검증하는 테스트 이다. 예를 들자면, 컴포넌트 안에서 렌더링이 정상적으로 되는지만을 확인한다면 Unit Test이지만, 이 컴포넌트가 Recoil 혹은 Zustand 등의 상태관리 라이브러리와 통합되었을 때 잘 어우러져서 우리가 원하는 결과를 내는지 테스트 하는 것. 이렇게 모듈을 통합하는 과정이 필요하기 때문에 유닛 테스트에 비해 비용이 많이 든다.

 

End-to-End Test :: E2E Test(E2E 테스트)

E2E 테스트는 실제 사용자가 웹/앱을 사용하는것과 굉장히 비슷한 환경을 구축한 후에 그 동작을 흉내 내어 테스트 하는 것이다. 사용자의 흐름을 비슷하게 테스트할 수 있지만, 환경을 구축하고 사용자 시나리오도 구축을 해야해서 비용이 많이들고 복잡성이 높다. 그렇기 때문에 빈번하게 수행하기 어렵고, 꼭 필요한 경우에만 실행하는 것이 일반적이다. 프론트엔드 같은 경우, 회원가입 전체 흐름을 테스트 한다고 했을 때, 실제 브라우저와 유사한 환경을 구축하고 그 안에서 여러 이벤트(값 입력, 인증 등)를 발생시켜 테스트를 한다.

 

프론트엔드 테스트 도구

프론트엔드에서 소프트웨어 테스트를 하기 위한 라이브러리들이 개발되어있다. 그 중 Jest, Mocha, Chai가 대표적이고, 특히 Jest는 한 주에 약 1800만회의 다운로드가 발생한다. 사실 Jest같은 경우는 CRA에서 기본적으로 포함해서 구성이 괴어있기 때문도 있고, 표준으로 사용되고 있다. 그래서 Jest를 사용하는 방법을 익히고 이를 활용한 몇 가지 예제를 보려고 한다.

 

Jest 사용법

우선 기본적으로 *.test.*의 형태 파일을 테스트 파일로 인식하고, 이 파일 속 코드를 실행한다. 일반적인 테스트 과정은

  1. 특정한 동작 수행
  2. 그 결과가 기대한 상황과 일치하는지 확인

이렇게 된다. 테스트 코드 작성도 마찬가지다.

 

Jest에서는 이렇게 판단하는 함수를 matchers라고 표현한다. 위 테스트 과정에 적용하면,

  1. 특정한 동작 수행
  2. matcher을 통해 기대한 값이 실제 값과 일치하는지 확인

이때, 특정 동작을 수행하기 위해 test() 혹은 it()함수를 활용한다. 아래 처럼 말이다.

// test(”테스트 이름", callback)
test('one plus three is four', () => {

// expect(실제 결과 값).matcher()
  expect(1 + 3).toBe(4);
});

// it(”테스트 이름", callback)
it('one plus three is four', () => {

// expect(실제 결과 값).matcher()
  expect(1 + 3).toBe(4);
});

 

만약 이 중 하나라도 기대값이 일치하지 않는다면, 이 테스트는 실패로 간주한다.

 

자주 사용하는 Jest의 matcher

  1. toBe
    • expect의 인자가 toBe의 인자와 일치하는지를 검사한다.
  2.  toEqaul 
    • Object의 경우 참조값이 다르기에, toBe를 활용할 경우 실제 각 객체의 내용이 같더라도, 일치하지 않다고 판단되게 된다. 따라서 객체를 상호 비교할 때는 toEqaul을 활용해야한다. toEqaul은 객체의 각 요소를 재귀적으로 검사하면서 두 객체가 동일한지 판단해준다. 
  3. toBeNull, toBeUndefined
  4. toBeGreaterThan, toBeGreaterThanOrEqual, toBeLessThan, toBeLessThanOrEqaul
    • 숫자값을 검증할 때 유용하게 사용할 수 있는 matcher
  5. toContain
    • Iterable한 객체들이 특정한 요소를 포함하고 있는지 검증할 때 사용할 수 있다.
  6. not
    • matcher의 기대값을 반대로 변경해준다.

 

이렇게 작은 단위에서 다양한 방법으로 테스트가 가능하다.

 

TDD에 대하여

테스트를 중심으로 개발하는 것이 중요해지고 있다. 이를 표현하는 용어가 TDD인 것이고. TDD는 Test-Driven-Development의 약자로,소프트웨어를 개발하는 여러 방법론 중 하나이다.

 

일반적인 개발 흐름은 코드작성 → 테스트코드 작성의 흐름을 따르지만, TDD의 핵심은 기존에는 테스트 코드를 먼저 작성하고, 그 후에 실제 코드를 작성하는 것이다. 즉, TDD는 실제 코드를 작성하기도 전에 테스트 코드부터 작성을 시작한다.

 

TDD는 크게 Red-Green-Blue 3가지 단계를 거치게 된다.

  1. Red: 실제 구현을 하기 전에, 먼저 실패하는 테스트 코드를 작성한다. 그 후 테스트를 실행한다. 실제 코드가 작성되지 않았기에 테스트 코드는 당연히 실패한다.
  2. Green: 테스트를 통과하기 위해 가장 간단한 형태로 코드를 작성한다. 그 후 테스트를 실행한다. 테스트는 실제 구현이 되었기에 통과한다.
  3. Blue: Green 단계의 코드를 더 좋은 형태로 리팩터링한다. 이 과정에서 지속적으로 테스트를 실행해서 테스트가 통과하는지 확인한다.

이러한 형태로 개발하게 되면 여러 가지 장점이 있다.

 

  1. 코드의 동작이 명확해진다.
  2. 구현을 잘못했을 경우 바로 확인이 가능하다.
  3. 코드 작성 과정에서 확신을 얻을 수 있다.

 

위와 같은 장점 외에도 더 많은 이점이 있다.

 

결론

테스트 코드를 작성하는 것은 여러가지 측면으로 보았을때, 아주 좋은 점들이 많다. 다만, 초기에 테스트를 하기에는 그 비용이 들기 때문에 그 부분도 적절히 고려해가면서 테스트를 하는 것이 핵심인 것 같다. 그리고 리액트를 테스팅해주는 라이브러리가 있는 것도 알게되었는데, 이 부분은 추후에 테스트 코드를 작성하게 되었을 때 조금 더 자세히 이야기 해볼 생각이다.