Unit Test

DevMinion·2022년 7월 22일
1

Unit Test에 대해서 예제와 함께 알아보쟈🙌
작년 2학년 2학기 소프트웨어공학 수업을 들으며 Unit Test와 TDD(Test Driven Development)에 대해서 배웠던 기억이 있다.
가고싶은 여러 산업체의 iOS개발자 분야 필수 요건중 Unit Test를 사용해보고 프로젝트에 적용해본 경험이 있는지 물어보는 회사가 자주 보였다.
막연히 Unit Test가 복잡하고 어렵다는 편견에 시도해본 적 없었는데 이번에 Unit Test를 공부해보며 지레 겁먹었던 과거를 후회했다.
그리고 이전에 진행한 프로젝트가 얼마나 쓰레기덩어리인지 알아버렸다.

Unit Test란

Unit Test란 말 그대로 단위 테스트를 말한다. 구현한 함수 단위로 정상동작하는지 테스트를 해볼 수 있다.

오늘 진행할 예제를 미리 소개하자면,
우리가 보통 어느 서비스에 회원가입을 하면 비밀번호의 생성 조건이 있다는 것은 모두가 잘 알고 있을 것이다.
예를 들어 대문자, 소문자, 숫자, 특수문자 조합. 이걸 만들어보자.

프로젝트를 만든다. 프로젝트를 만드는 시점에서 Include Tests를 체크한다.

이렇게 프로젝트를 만들면 아래와 같이 테스트 파일이 추가된다.

이미 만든 프로젝트가 있어서 예시로 만들어 이름이 aa...
여기 [프로젝트이름]Tests에 테스트하고자 하는 기능의 이름+Tests로 테스트를 추가한다. cmd+N을 누르면,

이렇게 Unit Test Case Class를 추가할 수 있다.

import XCTest

class LoginValidatorTests: XCTestCase {

    override func setUpWithError() throws {
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    override func tearDownWithError() throws {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }

    func testExample() throws {
        // This is an example of a functional test case.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
        // Any test you write for XCTest can be annotated as throws and async.
        // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
        // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
    }

    func testPerformanceExample() throws {
        // This is an example of a performance test case.
        self.measure {
            // Put the code you want to measure the time of here.
        }
    }

}

이것이 기본적인 Form이다. Test파일을 추가하면 자동으로 입력이 되어 있으니 걱정하지 말자.
이렇게 테스트를 할 수 있는 기본적인 세팅을 완료했다. 세부적인 함수를 알아보자.

setUpWithError()

Apple 공식 문서 - setUp
프로젝트를 생성하면 해당 함수 밑에 달리는 주석에 의하면,
테스트 코드가 실행되기 전에 먼저 실행이 된다. 즉, Unit Test를 진행하기 전에 미리 실행되어 테스트를 셋업(초기회)하는 코드가 여기에 담긴다고 생각하면 된다.

tearDownWithError()

Apple 공식 문서 - tearDown
프로젝트를 생성하면 해당 함수 밑에 달리는 주석에 의하면,
테스트 코드가 실행된 후에 실행이 된다. 즉, Unit Test가 끝나고 테스트에 사용한 것들을 깔끔하게 청소해주는 역할을 한다.

testExample()

우리가 테스트하길 원하는 것들을 테스트하는 코드를 작성하는 곳이다.

testPerformanceExample()

말 그대로 성능에 관한 코드-> 시간등을 확인할 수 있는 코드를 작성하는 곳이다.

이제 실제로 테스트를 해보자.

예제

먼저 아까 만들어둔 Test파일에 테스트할 프로젝트를 import해준다.

@testable import UnitTestPractice

testable 어노테이션을 붙히고 [프로젝트 이름]를 추가한다.

이제 테스트해볼 기능을 만들어보자. 간단하게 Swift파일을 만들고 Class를 하나 추가한다.

class PasswordFormValidator {
    
    func isValidPassword(password: String) -> Bool {
        let passwordRegEx = "^(?=.*[A-Za-z])(?=.*[~!@#$%^&*])(?=.*[0-9]).{8,16}"
        let passwordTest = NSPredicate(format: "SELF MATCHES %@", passwordRegEx)
        return passwordTest.evaluate(with: password)
    }
}

여기서 passwordRegEx속 오타같은 친구들은 비밀번호 정규식이다. 정규식은 따로 포스팅을 작성하겠다!
해당 식을 풀어보자면, 대문자 A부터 Z까지 포함, 소문자 a부터 z까지 포함, 특수문자 ~!@#$%^&* 포함, 숫자 0부터9까지 포함 그리고 8자이상 16자 이하.

이제 간단하게 User 구조체를 만들어 테스트에 사용해보자. User의 구조는 아래와 같다.

struct User {
    let password: String
    
    init(password: String) {
        self.password = password
    }
}

이제 테스트 파일의 코드를 작성해보자.

class PasswordValidatorTests: XCTestCase {
    
    // sut: SystemUnderTest
    var sut: PasswordFormValidator!

    override func setUpWithError() throws {
        sut = PasswordFormValidator()
    }

    override func tearDownWithError() throws {
        sut = nil
    }
    
    func testRegisterValidator_WhenValidPasswordProvided_ShouldReturnTrue() {
        // Given
        let user = User(password: "aASs223d3!")

        // When
        let isValidPassword = sut.isValidPassword(password: user.password)
        
        // Then
        XCTAssertTrue(isValidPassword, "대문자, 소문자, 숫자, 특수문자를 조합하여 8자 이상 16자 이하 이어야 합니다.")
    }
}

코드를 차근차근 설명하자면,

var sut: PasswordFormValidator!

sut란 SystemUnderTest의 약자로 테스트할 클래스를 정의할 때 사용한다.
우리는 sut에 PasswordFormValidator 인스턴스를 담았다.

이제 setUp과 tearDown에서 초기화 및 정리 코드를 작성한다.

이제 테스트 코드를 적는다. 나는 기본적으로 들어있는 testExample()를 사용하지 않고 따로 함수를 만들어 사용했다. 이름의 규칙이 있다고 들어 해당 규칙에 맞게 이름을 작성했다.
테스트할 기능_When+조건_예상 결과 <- 이런 느낌.
그리고 Given - When - Then 순으로 코드를 작성한다.

Given

테스트할 객체를 생성한다.
내 코드의 경우 User인스턴스를 만들고 password를 생성했다.

When

테스트할 코드를 작성한다.
isValidPassword를 만들어 여기서 테스트를 진행하고 결과를 반환받는다.

Then

테스트의 결과를 출력한다.
When에서 반환받은 Bool값을 XCTAssertTrue를 통해 True면 성공, False면 뒤에 붙은 메세지를 출력하고 실패함을 알린다.

결과

이제 실제 테스트를 돌려보자. 테스트는 간단하다.

여기 class선언 옆에 자기를 눌러달라고 외치는 버튼이 하나 있다. 눌러보자.

이렇게 테스트에 성공하면 초록색 체크표시가 뜨며 테스트를 통과했음을 알려준다. 예제에서 Given에 작성한 코드를 보면, password를 "aASs223d3!"으로 초기화한 것을 알 수 있다. 해당 비밀번호는 내가 작성한 비밀번호 정규식에 모두 부합하는 비밀번호이다. 그럼 이번엔 비밀번호를 부합하지 않게 작성하고 테스트 해보자. password를 "aaass221"로 생성해본다. 대문자가 없고 특수문자도 없다.

이렇게 보기 싫게 생긴 빨간 X표시로 변하며,

이렇게 False이기에 메세지를 출력한다.

마치며

이렇게 Unit Test를 공부하고 직접 예제를 통해 이해해 보았다. Unit Test를 몰라 직접 만든 기능을 빌드를 통해 손수 값을 입력하여 테스트를 하던 이전 프로젝트들이 눈에 아른거린다. 이제 Unit Test를 통해 보다 안전하고 빠르게 테스트를 진행해야 겠으며, TDD을 공부하여 다음 토이 프로젝트에는 이를 통해 개발해보고자 한다.

References(Thx🤙)

XCTest - Apple
[iOS] Unit Test에 대해서 간단히 알아보기 - Fomagran

profile
iOS를 개발하는 미니언

0개의 댓글