Danny의 iOS 컨닝페이퍼
article thumbnail

유닛 테스트(Unit Test)

사용하려는 기능이 개발자의 의도에 맞게 동작하는지 테스트를 하는 작업입니다.

 

Unit Test에는 간단하게 "FIRST"라는 원칙이 있다고 합니다.

 

속도 (Fast)

느린 테스트는 개발자가 코드를 수정하고 결과를 확인하기까지 시간이 많이 걸리므로 생산성을 저하시키죠.

따라서 유닛 테스트에서는 빠른 속도로 테스트를 수행할 수 있도록 설계해야 합니다.

 

독립적 (Independent / Isolated)

유닛 테스트는 각각의 테스트가 서로 독립적으로 실행될 수 있어야 합니다.

이것은 특정 테스트의 결과가 다른 테스트에 영향을 미치지 않고 독립적으로 실행한다는 것을 의미합니다.

이러한 방식으로 작성된 유닛 테스트는 코드 변경에 대해 신속하게 반응할 수 있습니다.

 

반복적 (Repeatable)

유닛 테스트는 반복적으로 실행될 수 있어야 합니다.

동일한 입력값에 대해서는 항상 동일한 결과를 보여야 하며, 실행 순서나 실행환경이 달라져도 동일한 결과를 보여주어야 합니다.

이러한 특징은 테스트의 신뢰성을 높일 수 있습니다.

 

자동화 (Self-Automation)

자동화란, 테스트를 실행하고 결과를 확인하는 모든 과정을 자동으로 처리하는 것을 의미합니다.

수동으로 테스트를 수행하는 것보다 자동화된 테스트를 수행하면 시간과 비용을 절약할 수 있습니다.

또한 자동화된 테스트는 개발자가 코드를 변경하거나 기능을 추가할 때마다 쉽게 실행할 수 있어서 버그를 신속하게 찾을 수 있습니다.

 

시점 (Timely)

코드를 수정을 했을 때 해당 수정으로 인해 다른 기능이 영향을 받는지 확인하기 위해서

즉, 코드가 수정되어 다른 부분에서 영향을 미치지 않았는지 확인할 수 있는 시점에 테스트를 실행해야 합니다.

(ex. 수정된 코드를 다른 부분에서 호출하기 전에 유닛 테스트를 실행)

 

코드 수정 후 변경 사항을 더욱 빠르게 확인할 수 있도록 도와줍니다.

 

 

생성 방법

3가지 방법으로 유닛 테스트 생성이 가능합니다.

 

방법 1

처음 프로젝트를 만들 때 Inclide Tests를 눌러주면 자동으로 생성이 됩니다.

 

방법 2

프로젝트 파일 선택 → Editor → Add Target → Unit Testing Bundle 클릭

 

방법 3

Test Navigator를 통한 생성이 있습니다.

 

원하는 테스트 이름을 지어주면 생성완료!

 

 

간단 설명

XCTest란 실제 유닛 테스트를 할 수 있게 도와주는 프레임 워크입니다.

또한 XCTest는 각각의 테스트가 서로 영향을 미치지 않도록(독립적으로 실행되도록) 메서드를 통해 테스트를 도와주죠.

 

 

XCTest의 프레임 워크에서 제공되는 메서드들

 

class func setUp()

func setUpWithError(), func setUp()

func testMethod() & addTestdownBlock(_:)

func testDown(), func tearDownWithError()

class funs tearDown()

 

이와 같이 여러 메서드들이 존재하지만 여기서는 두 가지만 다루겠습니다. (setUp, tearDown)

 

func setUp()

기본값을 생성할 때 사용됩니다. (객체 인스턴스 생성, db 초기화, 규칙을 작성 등)

// 각각의 테스트 메서드가 실행되기 전에 setUp이 먼저 실행되어 값을 재설정 시켜줍니다.
override func setUp() {
    super.setUp()
}

 

func tearDown()

초기 상태로 복원할 때 사용됩니다. (파일 닫기, 연결, 새로 만든 항목 제거, 트랜잭션 콜백 호출 등)

// 만약 테스트에서 어떤 값을 변경했을 경우 다음 테스트에서 문제가 발생할 수 있기 때문에 사용
// 각각의 유닛 테스트가 끝난 후 테스트 값을 처리합니다.
override class func tearDown() {
    super.tearDown()
}

 

XCTest사용되는 전체 메서드들의 동작 순서입니다.

 

 

XCTest의 프레임 워크에서 제공되는 XCTAssert 메서드들 (결과 값을 검증하는 방법)

이를 이용해 테스트 케이스를 작성하고 테스트할 수 있습니다.

// 두 값이 같은지 or 다른지 확인합니다.
XCTAssertEqual(expression1: T, expression2: T, message: String)
XCTAssertNotEqual(expression1: T, expression2: T, message: String)
// expression1 테스트 대상 실제 값
// expression2: 예상 값
// message: 실패 시 출력할 메시지

// 두 실수 값이 지정한 정확도 범위 내에 있는지 확인합니다.
XCTAssertEqual(expression1: T, expression2: T, accuracy: T, message: String)
XCTAssertNotEqual(expression1: T, expression2: T, accuracy: T, message: String)
// expression1 테스트 대상 실제 값
// expression2: 예상 값
// accuracy: 허용 오차 범위(expression1 과 expression2의 최대 차이)
// message: 실패 시 출력할 메시지

// 조건이 참인지 거짓인지 확인합니다.
XCTAssertTrue(expression: Bool, message: String)
XCTAssertFalse(expression: Bool, message: String)
// expression: 테스트 대상 조건
// message: 실패 시 출력할 메시지

// 값이 nil인지 아닌지 확인합니다.
XCTAssertNil(expression: Any?, message: String)
XCTAssertNotNil(expression: Any?, message: String)
// expression: 테스트 대상 값
// message: 실패 시 출력할 메시지

 

 

실행 방법

들어가기 앞서

아래의 3가지 방법을 통해 테스트를 실행할 수 있습니다.

 

방법 1. Commnad + u

방법 2. Test navigator에서 실행버튼 클릭

방법 3. 코드창에서 다이야 모양 클릭

 

바로 만들어봅시다.

 

테스트 코드를 모두 지우고

새로 계산기(+, -)에 대한 유닛테스트를 만들어 보겠습니다.

 

일단 "FIRST"원칙과 같이

시점(Timely)의 따라 코드를 호출하기 전에 유닛 테스트를 만들어 줍니다.

 

먼저 더하기에 대한 유닛테스트를 진행해 봅시다.

우리는 2+3의 결괏값을 알고 있으니 XCTAssertEqual를 통하여 이와 같이 테스트를 만들어 줬습니다.

import XCTest

final class UnitTestCalc_DemoTests: XCTestCase {

    // 더하기 테스트
    // 보통 테스트를 작성 시 이와 같이 "test_"로 시작하여 테스트할 이름을 입력해줍니다.
    func test_AddTwoNumbers() {
        
        let calculator = Calculator()
        // 두 숫자의 계산값을 리턴 받는 함수를 생성
        let result = calculator.add(2, 3)
        
        // 값이 같은지 여부를 확인 합니다.(계산결과, 내가 생각하는 맞는 결과값, 실패시 메세지)
        XCTAssertEqual(result, 5, "결과값은 5가 아닙니다.")
    }
}

아직까지는 Xcode에서 객체로 사용할 Calculator도 없고 add 메서드도 없어서 

빨간색에러로 피를 뿜고 있을 겁니다. (또한, 타겟도 지정해줘야 하죠!)

 

그럼 바로 클래스 Calculator를 만들고 add 메서드도 추가해 봅시다.

class Calculator {
    
    func add(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
}

 

⭐️ 중요 ⭐️ 

유닛테스트를 진행하기 위해 타겟을 지정해야 합니다. (특정 앱 대상에서 사용할 수 있게 설정)

타겟 지정방법은 2가지 방법이 있습니다.

 

방법 1. File Inspector에서 타겟을 지정해 준다.

 

방법 2. @testable을 통해 해당 모듈에 접근 (보통 이 방식을 사용함)

@testable : 모듈 간 접근성 제어하기 위한 속성입니다.

(internal, fileprivate, private 멤버에 접근하여 테스트를 가능하도록 도와줍니다.)

만들어 놓은 유닛 테스트 모듈에서 사용합니다.

// @testable import 모듈(프로젝트)
// 해당모듈에 접근하여 멤버인 메서드들을 사용하게 도와주는 키워드입니다.

import XCTest
@testable import UnitTestCalc_Demo

final class UnitTestCalc_DemoTests: XCTestCase {}

 

@testable로 모듈을 불러왔으니, 이제 진짜로 준비가 완료 됐습니다.

실행을 해보겠습니다 (Commnad + u)

 

만약 테스트가 성공하면 이와 같이 오른쪽에 초록색 틱 모양이 생성이 됩니다.

 

이번에는 계산과 다른 값인 6을 넣어 테스트해 보겠습니다.

테스트 실패 시 빨간색 X표와 함께 에러가 발생합니다.

Debug창 - error: XCTAssertEqual failed: ("5") is not equal to ("6") - 더하기 비교 결과값이 다름

 

 

유닛 테스트 리펙토링

이제 간단한 사용방법을 알아봤으니 유닛 테스트에 대한 리펙토링을 실시해 봅시다.

 

빼기 테스트까지 추가하니 이와 같이 중복되는 코드들이 보입니다. let calculator = Calculator()

import XCTest
@testable import UnitTestCalc_Demo

final class UnitTestCalc_DemoTests: XCTestCase {
    
    func test_SubtractTwoNumbers() {
    
        let calculator = Calculator()
        let result = self.calculator.subtract(5, 2)
        
        XCTAssertEqual(result, 3, "빼기 비교 결과값이 다름")
    }
 
    func test_AddTwoNumbers() {
        
        let calculator = Calculator()
        let result = self.calculator.add(2, 3)
        
        XCTAssertEqual(result, 5, "더하기 비교 결과값이 다름")
    }
}

 

setUp, tearDown 메서드와 함께 사용하여 리펙토링을 해 봅시다.

코드의 중복도 없고 깔끔해진거 같네요.

import XCTest
@testable import UnitTestCalc_Demo

final class UnitTestCalc_DemoTests: XCTestCase {
    
    private var calculator: Calculator!

    // 각각의 테스트 메서드가 실행되기 전에 setUp이 먼저 실행되어 값을 재설정 시켜줍니다.
    override func setUp() {
        super.setUp()
        
        // 초기화를 해줍니다.
        self.calculator = Calculator()
    }
    
    func test_SubtractTwoNumbers() {
    
        let result = self.calculator.subtract(5, 2)
        XCTAssertEqual(result, 3, "빼기 비교 결과값이 다름")
    }
 
    func test_AddTwoNumbers() {
        
        let result = self.calculator.add(2, 3)
        XCTAssertEqual(result, 5, "더하기 비교 결과값이 다름")
    }
    
    // 각각의 유닛 테스트가 끝난 후 테스트 값을 처리합니다.
    override class func tearDown() {
        super.tearDown()
    }
}

 

 

 

완전 기본적인 유닛 테스트 사용법이지만 모두에게 도움이 되셨길 바랍니다.
추후에 더 심화된 내용으로 글을 올리겠습니다.

부족한 설명이지만, 조금은 이해 가셨나요?

틀린 내용이 있다면 언제든지 지적해 주시면 감사히 받겠습니다. 🫠
읽어주셔서 감사합니다 😃

 

 

참고

XCTestCase

Equality and Inequality Assertions

UnitTest의 사용법

XCode XCTest 테스트 메서드 실행 순서 정리

 

 

반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!