:sweat: unit test 작성해야하는데 test 파일 앞에 앉아 뭘 써야되는지 1도 몰으겠는 나와 같은처지에 있는 분을 위해 작성한 글입니다.
작성한 코드 중에, 테스트 대상인 의미있는 부분을 실행해보는, 개발자가 작성한 코드 조각이다.
이제 Test 필요성은 알겠는데 뭘 테스트 하라는거야…. 할 때 생각해 볼 질문들
'무엇을 테스트해야 하는가?' 에서 '무엇'을 생각나게 해줄 질문들
코드가 내가 예상한 결과대로 나오는지 결과의 유효성 검사를 한다. 즉, 생각한대로 동작한다는 것을 증명하는 과정이다.
코드가 옳게, 맞게 동작하는지 알 수 있다는 것은 이미 요구사항과 예측되는 결과를 파악하고 있다는 의미이다.
따라서 내가 짠 코드인데 맞게 동작하는지 아닌지 모른다면 코드랑 테스트코드를 짜는 것 자체가 시간낭비라고 한다. :fearful:
대부분의 버그가 보통 경계에 존재한다. 에러가 발생할 가능성이 큰 케이스를 테스트 해본다.
경계값 example
가능한 경계조건을 떠올릴 때, CORRECT 만 기억하자! :star:
(자세한 CORRECT 항목에 대한 내용은 밑에 있다)
논리적 역 사용을 한다.
p -> q 방법을 테스트 할 땐, 반대로 q -> p 해서 처음의 값이 나오면 테스트 성공~
Example
매개변수의 제곱을 return하는 함수가 제대로 작동하는지 테스트
제곱한 수의 제곱근 == 원래 수
func getSquare(of num: Double) {
return num * num
}
// unit test
func testGetSqure() {
Double num = 4.0
Double squareNum = getSquare(of: num)
XCTAssertEqual(num, sqrt(squareNum))
}
유의할 점 : 두 루틴의 공통 에러가 있을 수 있음에 유의
다른 수단, 방법을 이용해서 메소드의 결과를 교차확인하는 검증 방법
어떤 값을 계산하는 방법은 여러가지 존재한다.
내가 방법1로 A라는 값을 구하면, A라는 결과를 내는 다른 방법2로 검증하는 방법이다.
Test 대상 메소드 이외의 다른 방법으로 구하는 메소드를 사용해서 결과가 같은지 확인하는 방법
Example
매개변수의 제곱값을 return 하는 함수 테스트
제곱값을 구하는 다른 방법 : swift library - pow()를 사용한 값과 같은지 확인
func getSquare(of num: Double) {
return num * num
}
// unit test
func testGetSqure() {
Double num = 4.0
Double testSqure = getSquare(of: num)
Double librarySqure = pow(num, 2)
XCTAssertEqual(testSqure, librarySqure)
}
각 방법별로 부분 합을 구해 전체 합이 맞는지 확인하기
에러 조건을 강제로 만들어 내어, 해당 상황에서 적절히 핸들링, 처리 되는지 확인하는 방법
경계 값 케이스를 좀더 세분화 해서 질문해보기
특정 형식을 따르는 데이터를 형식에 맞게 처리하거나 생성할 수 있을까?
형식에 맞게 데이터를 잘 처리 하거나 잘 체크하는지 테스트한다.
형식에 맞지 않을 때 처리 방법도 생각해야 한다.
큰 데이터 모음에서 의도한 순서나 위치대로 데이터가 들어가는가?
Example
ladder game에서 step(Bool)을 연속해서 나오게 하지 않기로 구현했다면, 이를 검증하는 테스트가 필요. 연속해서 step이 있는 사다리에 대해서 적절한 처리가 필요하다.
func testCreateStepsInRow() {
let steps: [LadderStep] = ladderGame.createStepsInRow()
for i in 1..<steps.count {
XCTAssertEqual(steps[i-1].exists && steps[i].exists, false)
//true가 연속으로 나오면 fail
}
}
정렬된 상태로 항상 유지되어야 하는 데이터라면, 제대로 된 순서로 정렬되어 있는지 검증하는 테스트가 필요하다.
해당 변수가 의도에 맞는 범위의 값만 가지는가?
어떤 변수형이 필요하거나 원하는 값보다 더 크게 범위를 허용하는 상황을 말한다.
Example
나이를 표현하는 변수인데 Int로 선언해서 받는 경우, 음수나 2000살도 허용할 수 있게 된다
좋은 객체 지향 설계에서는 이런 경우 단순히 기본형을 사용하지 않고 범위를 의도한대로 유지해주는 코드가 구현되어 있어야 한다.
메서드가 자신이 속한 객체가 아닌, 외부의 객체를 참조할 경우 해당 메서드가 제대로 동작할까?
자기 영역을 벗어난 외부의 객체를 참조할 경우, 이 객체에 따른 메서드의 동작도 테스트 해야한다.
"주어진 것이 존재하는가?"
넘겨받거나 가지고 있는 모든 값에 대해 그 값이 존재하지 않거나, nil이거나, 0이라면?
개수 세기
ex. player가 n명일 때, ladder의 step 개수는 n-1이어야 한다.
ex. 최근 순 주문 10개씩 전송하는 프로그램 - 언제 전송해야 하는지, 10개가 안채워져도 전송할 수 있는 지 등...
메서드들 순서가 뒤섞여 호출시에 어떤 일이 일어날까?
좋은, clean한 unit test의 특성 - FIRST
unit test는 실행속도가 빨라야 함. 그래야 자주 실행해보고 확인할 수 있다.
unit test는 각 test method가 독립적이어야 한다. 즉, 서로에게 영향을 주거나 상호의존적이면 안된다.
문제가 하나라면 한 method만 통과 못하도록 해야함.
서로 의존적이라면 한 test method의 fail이 연속적인 여러개의 test method를 fail로 이어질 수 있다.
test 하는 목적이 error의 위치를 쉽게 파악하기 위함인데 이러면 test하는 의미가 없다.
어떤 환경에서든 repeatable해야 한다. (반복해서 실행할 수 있어야 한다)
니 컴에서 안돼? 내 컴에서는 되는데? :x:
test는 boolean output을 내야 함 - pass or fail
Test 성공했는지 여부를 성공/실패로 알아야지 log 파일을 매번 확인해서 알 수 있으면 망:poop:이라는 뜻
시기적절하게.. happens at a moment when it's useful, effective, or relevant
그 시기 = production code 작성 전
Test code 작성 후, production code 작성!
순서가 바뀌면 production code가 test 불가능한 코드가 되어있을 수도 있다..