[Test] What's good test code?

Falcon·2023년 5월 29일
1

javascript

목록 보기
23/28
post-thumbnail

테스트 코드는 언제 쓰나

  • 테스트 코드는 문서다.
  • 테스트 코드 또한 필요할 때 추가한다.

항상 발생할 수 있는 모든 시나리오에 대한 테스트를 강박적으로 다 쓰고, 테스트 커버리지를 100%로 맞추려고 하는 것은 오버엔지니어링이 될 수 있다.
발생 가능할법한 상황에 대한 테스트를 충분히 써라.

테스트 코드는 왜 쓰나

  • 특정 기능이 정상적으로 동작한다는 것을 보장하기 위해.
  • 유지 보수 관리 측면
  • 테스트 코드 == 문서

README.md 나 wiki, Swagger API docs 에는 담기지 않는 비즈니스 로직의 흐름이 테스트 코드에 담길 수 있다.

어떻게 쓰나.

given (Input) - when (조건) - then(output) 으로 쓰기도 하고
Arrange - Act (행위) - Assert(기대값) 으로 쓰기도한다.

용어만 조금 다를 뿐 다음 맥락으로 한줄 정리가 가능하다.

입력값 -> 조건 -> 기대값

테스트 코드도 개발하면서 Interative 하게 추가된다.

Tips

expect() 메소드는 사실 true/false 를 리턴하지 않는다
테스트 기대값에 부합하지 않은 경우 Throw Error 처리한다.

describe('Calculator', ()=>{
  const calculator = new Calculator()

  test('add - should yield 0 if an empty array is provided', ()=>{
    // Arrange
    const nums = []
    // Act
    const res = calculator.add(nums)
    // Assert
    expect(res).toEqual(0)
  })
)

What's Good Test code?

무엇을 테스트하고, 무엇을 테스트하지 않을지 결정해보자

오로지 내 코드만 테스트한다.
third-party 라이브러리 코드는 절대 테스트하지 않는다.
내가 변경(Controll)할 수 없기 때문이다.

Not test

  • 클라이언트 코드로 서버를 테스트하기
  • Native browser 코드 테스트
  • Native nodejs 코드 테스트
  • 무지성 커버리지 100% 달성을 위한 테스트
    이미 테스트가 완료된 코드를 단순 호출하는 코드같은 경우 테스트가 필요없다. 이런 경우 테스트 코드를 쓰는게 낭비가 된다.
    테스트 커버리지 100% 자체를 목적으로 둬선 안된다.

Good Test

  • 내가 만든 클래스의 모든 메소드 단위 테스트
  • 내가 만든 Controller 의 호출 통합 테스트
  • 요구사항에 대한 경계값 테스트 Haapy & Bad Case \
    이상,이하,초과,미만, 구간 등
  • Only test one thing
    오로지 하나의 상황에 하나씩만 테스트하라.
  • expect() (Assertion) 을 최소화하라
    되도록 하나의 테스트에 1개의 expect() 만 쓰는게 좋다.
    이유는 expect() 를 많이 쓸 수록 One by One 규칙에 위배될 확률이 높기 때문이다.

클라이언트 코드 테스트를 원하면 response / error 상황을 mocking 하여 해결해야 한다.

너무 당연하게도, 테스트하기 좋은 코드는 쪼개진 코드다.
당연히 하나의 함수가 하나의 책임 (기능)을 하는 코드를 만들었을 때 테스트하기도 좋고 유지보수 하기도 좋아진다.

하나의 메소드 안에 여러 다른 메소드 호출 (dependencies) 이 존재하는 경우, unit test 하기 어렵다. 이럴 땐 mocking 으로 문제를 풀 수도 있다.


Integration test

통합테스트가 뭔지 의견이 분분한데 그냥 여러 unit, 여러 function call 을 테스트하면 통합테스트로 볼 수 있다.

통합 테스트는 왜 필요한가?

Unit test 를 성공했다는 것은 각각의 unit 이 예상한대로 작동한다는 것이다.
서버든 클라이언트는 우리가 원하는 시나리오가 있을 것이다.

예를 들면 '상품 구매' 라는 시나리오가 있다고 치자.
상품 구매 시나리오는 다음 4단계로 나눌 수 있다.
1. 상품 구매 요청
2. 재고 확인
3. 결제 처리
4. 물품 수령

각 단계를 처리하는 메소드

function requestGoodsPurchase() : boolean {}
function isValidateStock() : boolean {}
function payment() : boolean{}
function receiveGoods() {}

위 4개의 함수가 각각 unit test 가 통과됐다 하더라도
다음과 같이 통과된 unit (function) 을 조합하는 과정에서 비즈니스 로직이 올바른지 테스트 해볼 필요가 있다.

// 상품 구매 시나리오 
const isValidReq = requestGoodsPurchase()
if (!isValidReq) new throw Error('Invalid purchase request')
const isAvailable = isValidateStock()
if (!isAvailable) // ..

payment();
receiveGoods();
profile
I'm still hungry

0개의 댓글