컴퓨터 프로그래밍에서 소스 코드의 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차다. 즉, 모든 함수와 메소드에 대한 테스트 케이스(Test case)를 작성하는 절차를 말한다
Unit Test first 원칙
Unit Test을 해야하는 이유
Unit Test의 좋은 방법
// LottoMachine
struct LottoMachine {
func isValidLottoNumbers(of numbers: [Int]) -> Bool {
guard numbers.count == 6, Set(numbers).count == 6 else {
return false
}
for num in numbers {
guard 1...45 ~= num else {
return false
}
}
return true
}
func makeRandomLottoNumbersArray() -> [Int] {
var numberSet: Set<Int> = []
while numberSet.count < 6 {
let randomNumber = Int.random(in: 1...45)
numberSet.insert(randomNumber)
}
return Array(numberSet)
}
func countMatchingNumber(user: [Int], winner: [Int]) throws -> Int {
guard isValidLottoNumbers(of: user) && isValidLottoNumbers(of: winner) else {
throw LottoMachineError.invalidNumbers
}
let winNumbers = user.filter { winner.contains($0) }
return winNumbers.count
}
}
enum LottoMachineError: Error {
case invalidNumbers
}
// LottoMachineTests
class LottoMachineTests: XCTestCase {
var sut: LottoMachine!
override func setUpWithError() throws {
sut = LottoMachine()
}
override func tearDownWithError() throws {
sut = nil
}
//MARK: - isValidLottoNumbers
func test_isValidLottoNumbers_6개보다_많은입력_False() {
//given
let input = [1, 2, 3, 4, 5, 6, 7, 8]
// when
let result = sut.isValidLottoNumbers(of: input)
// then
XCTAssertFalse(result)
}
func test_isValidLottoNumbers_6개보다_적은입력_False() {
// given
let input = [1, 2, 3]
// when
let result = sut.isValidLottoNumbers(of: input)
//then
XCTAssertFalse(result)
}
func test_isValidLottoNumbers_입력이_1보다_작을때_false() {
// given
let input = [-1, 2, 3, 4, 5, 6]
// when
let result = sut.isValidLottoNumbers(of: input)
//then
XCTAssertFalse(result)
}
func test_isValidLottoNumbers_입력이_45보다_클때_false() {
// given
let input = [1, 2, 3, 4, 5, 50]
// when
let result = sut.isValidLottoNumbers(of: input)
//then
XCTAssertFalse(result)
}
func test_isValidLottoNumbers_값이_중복일때_false() {
// given
let input = [1, 2, 3, 4, 5, 5]
// when
let result = sut.isValidLottoNumbers(of: input)
//then
XCTAssertFalse(result)
}
func test_isValidLottoNumbers_값이_중복없이_6개입력_true() {
// given
let input = [1, 2, 3, 4, 5, 6]
// when
let result = sut.isValidLottoNumbers(of: input)
//then
XCTAssertTrue(result)
}
//MARK: - makeRandomLottoNumbersArray
func test_makeRandomLottoNumbersArray_6개의_중복없는_숫자반환_true() {
// given
let input = sut.makeRandomLottoNumbersArray()
// when
let result = sut.isValidLottoNumbers(of: input)
// then
XCTAssertTrue(result)
}
//MARK: - countMatchingNumber
func test_countMatchingNumber_4개의동일한숫자일때_4를반환_true() throws {
// given
let user = [1, 2, 3, 4, 5, 6]
let winner = [3, 4, 5, 6, 7, 8]
// when
let result = try sut.countMatchingNumber(user: user, winner: winner)
// then
XCTAssertEqual(result, 4)
}
}
테스트 코드를 먼저 작성하는 개발 방법론은 테스트 주도 개발(Test-Driven Development, TDD)로 많이 불린다.
테스트 코드를 먼저 작성하는 이유
- 실패하는 작은 단위 테스트를 작성한다. 처음에는 컴파일조차 되지 않을 수 있다.
- 빨리 테스트를 통과하기 위해 프로덕션 코드를 작성한다. 이를 위해 정답이 아닌 가짜 구현 등을 작성할 수도 있다.
- 그 다음의 테스트 코드를 작성한다. 실패 테스트가 없을 경우에만 성공 테스트를 작성한다.
- 새로운 테스트를 통과하기 위해 프로덕션 코드를 추가 또는 수정한다.
- 1~4단계를 반복하여 실패/성공의 모든 테스트 케이스를 작성한다.
- 개발된 코드들에 대해 모든 중복을 제거하며 리팩토링한다.
struct StrangeCalculator {
// TODO: TDD로 정수 타입을 파라미터로 받아, 그 절대값이 짝수이면 절반의 값을 반환하는 메서드 구현하기
// func returnHalfValueIfNumberIsEven(number: Int) -> Int {
// return 0
// }
// func returnHalfValueIfNumberIsEven(number: Int) -> Int {
// return number
// }
// func returnHalfValueIfNumberIsEven(number: Int) -> Int {
// if number % 2 == 0 {
// return number / 2
// }
//
// return number
// }
func returnHalfValueIfNumberIsEven(number: Int) -> Int {
let result = number % 2 == 0 ? number / 2 : number
return result
}
}
class StrangeCalculatorTests: XCTestCase {
// TODO: TDD로 정수 타입을 파라미터로 받아, 그 절대값이 짝수이면 절반의 값을 반환하는 메서드 구현하기
var sut: StrangeCalculator!
override func setUpWithError() throws {
try super.setUpWithError()
sut = StrangeCalculator()
}
override func tearDownWithError() throws {
try super.tearDownWithError()
sut = nil
}
func test_returnHalfValueIfNumberIsEven_0을입력했을때_0을반환() {
let input = 0
let expectation = 0
let result = sut.returnHalfValueIfNumberIsEven(number: input)
XCTAssertEqual(result, expectation)
}
func test_returnHalfValueIfNumberIsEven_5를입력했을때_5를반환() {
let input = 5
let expectation = 5
let result = sut.returnHalfValueIfNumberIsEven(number: input)
XCTAssertEqual(result, expectation)
}
func test_returnHalfValueIfNumberIsEven_10을입력했을때_5를반환() {
let input = 10
let expectation = 5
let result = sut.returnHalfValueIfNumberIsEven(number: input)
XCTAssertEqual(result, expectation)
}
}