[iOS] Unit test & UI Test

전성훈·2023년 12월 7일
2

iOS/TEST

목록 보기
1/3
post-thumbnail

주제: iOS 프로젝트에 단위, UI 테스트 적용하기


iOS Unit Test란?

  • Unit Test는 소프트웨어 개발 과정에서 주로 메서드의 기능을 검증하는 테스트입니다.
  • iOS 개발에서 Unit Test는 주로 Xcode와 같은 IDE에서 XCTest 프레임워크를 사용하여 구현합니다.
  • 이 테스트는 독립적으로 실행되어야 하며, 어떤 외부 상태에도 의존하지 않고, 다른 테스트에 영향을 주지 않아야 합니다.

테스트할 내용 파악하기

  • 어떤 테스트든 작성하기 전에, 기본적인 사항을 알아두는 것이 중요합니다.
  • 이는 기존 앱을 확장하고자 한다면, 변경할 예정인 모든 컴포넌트에 대한 테스트를 먼저 작성해야 합니다.
  • 일반적으로, 테스트는 아래와 같은 항목을 커버해야 합니다.
  1. 핵심 기능
    • 모델 클레스와 메소드 및 컨트롤러와의 상호작용
  2. 일반적인 UI 워크플로우
  3. 경계 조건(Boundary conditions)
  4. 버그 수정

테스트의 기본 원칙 (FIRST)

  • FIRST 원칙을 통해 효과적인 Unit Test를 위한 간결한 기준들을 설명합니다.
  • Fast
    • 테스트는 빠르게 실행되어야 합니다.
  • Independent/Isolated
    • 테스트들은 서로의 상태를 공유하지 않아야 합니다.
  • Repeatable
    • 테스트를 실행할 때마다 동일한 결과를 얻어야 합니다.
  • Self-Validating
    • 테스트는 완전히 자동화되어야 하며, 스스로 결과물이 옳은지 그른지 판단할 수 있어야 합니다.
  • Timely
    • 이상적으로, 테스트는 프로덕션 코드를 작성하기 전에 작성해야 합니다. 이는 Test-driven development (TDD)로 알려져 있습니다.
  • 테스트 주도 개발의 일반적인 절차는 아래와 같습니다.
  1. 실패하는 테스트 작성
    • 먼저, 구현할 기능에 대한 테스트 케이스를 작성합니다. 이 단계에서 작성된 테스트는 기능이 아직 구현되지 않았기 때문에 실패합니다.
  2. 성공 코드 작성
    • 실패한 테스트를 통과시킬 수 있는 최소한의 코드를 작성합니다.
  3. 리팩토링
    • 새로 작성된 코드와 기존 코드의 품질을 개선하고, 중복을 제거하는 등의 리팩토링을 수행합니다.

Xcode에서 Unit Testing

  • 시작하기 앞서 본 설명은 번호 찾기 게임코드를 기반으로 하고 있습니다.
  • 해당 코드의 내용은 아래와 같으며, 이는 랜덤 숫자를 서버에서 받고 해당 숫자를 맞추는 게임입니다.
  • 연속 맞춤에 따라 연속 성공의 count가 증가되고 이는 userdefaults으로 저장되고 다시 시작 버튼을 클릭 시 스테이지 및 기록을 초기화 할 수 있습니다.

iOS Unit Test 하는 방법

  1. XCTest 프레임워크 사용
    • iOS에서는 Xcode에 내장된 XCTest 프레임워크를 사용해 Unit Test를 작성합니다.
  2. Test Case 작성
    • XCTestCase를 상속받는 클래스를 만들고,test로 시작하는 메서드를 정의하여 각각의 테스트 케이스를 작성합니다.
  3. Assert 사용
    • XCTest에서 제공하는 다양한 assert 함수를 사용하여 예상되는 결과와 실제 결과를 비교합니다.
  4. Mock과 Stub 사용
    • 외부 의존성을 제거하기 위해 Mock 객체나 Stub을 사용합니다.
  5. Test Coverage
    • Xcode에서 제공하는 Test Coverage 도구를 사용하여 테스트 커버리지를 분석하고, 누락된 부분을 찾아낼 수 있습니다.

Unit Test Target 만들기

  • 해당 소스코드를 확인해서 Command-6을 통해 Test navigator를 엽니다.
  • 좌측 하단의 + 버튼을 눌러 New Unit Test Target을 생성합니다.
    Unit Test Target
  • 기본 템플릿은 FindNumberTests이며, 테스팅 프레임워크인 XCTest를 가져오고, XCTestCase의 서브클래스인 FindNumberTests 를 정의하며 setupWithError(), tearDownWithError() 및 기타 테스트 메서드들을 포함합니다.

  • setUpWithError()
    • 해당 메서드는 각 테스트 메서드가 호출되기 전에 실행됩니다.
    • 테스트 환경을 설정하는 코드를 여기에 작성합니다.
    • 예를 들어, 테스트에 필요한 객체를 초기화하거나 테스트 데이터를 준비하는 작업을 수행합니다.
  • tearDownWithError()
    • 해당 메서드는 각 테스트 메서드의 호출이 끝난 후에 실행됩니다.
    • 테스트에서 사용된 리소스를 정리하거나 해제하는 코드를 여기에 작성합니다.
    • 예를 들어, 객체를 nil로 설정하거나 파일 시스템에서 테스트 데이터를 삭제하는 작업을 수행합니다.
  • testExample()
    • 해당 메서드는 실제 유닛 테스트 케이스를 구현하는 곳입니다.
    • XCTAssert및 관련 함수를 사용하여 테스트 결과가 올바른지 검증합니다.
  • testPerformanceExample()
    • 해당 메서드는 성능 테스트 케이스를 구현하는 곳입니다.
    • measure 클로저 내에 성능을 측정하고자 하는 코드를 기입합니다.
    • 해당 메서드는 해당 코드 블록의 실행 시간을 측정하여 성능 분석을 할 수 있게 합니다.

FindNumberTests Code 작성하기

  • 우선, FindNumberTests.swift에 import XCTest아래에 다음 줄을 추가해야 합니다.
@testable import FindNumber
  • 이렇게 하면 유닛 테스트가 FindNumber의 내부 타입과 함수에 접근할 수 있습니다.
  • 그 후 FindNumberTests 함수 최상단에 아래의 변수를 추가해야 합니다.
var sut: FindNumberModel!
  • FindNumberModel은 테스트 대상(System Under Test, SUT)이며, 이 테스트 케이스 클래스가 테스트하는 객체입니다.
  • 다음으로 setupWithError() 메서드의 내용을 다음으로 대체해야 합니다.
try super.setupWithError() 

sut = FindNumberModel()
  • 이렇게 하면 테스트 클래스의 모든 테스트가 SUT 객체의 속성과 메서드에 접근할 수 있습니다.
  • 또한 잊지 말고, tearDownWithError() 에서 sut 객체를 해제해야합니다.
sut = nil 
try super.tearDownWithError()
  • 그 후 FindNumberModel 클래스에 맞는 테스트를 작성해야 하므로 example 메서드들은 삭제해야 합니다.

첫번째 테스트 작성하기 - 기능 테스트 1

  • 사용자가 계속해서 게임을 진행하다가 게임 초기화 버튼을 클릭 했을 때 현재 연속 달성 기록, 최대 연속 달성 기록, 현재 라운드를 제대로 초기화 하는지 확인하는 테스트를 진행해보겠습니다.
    func test_스테이지_초기화() {
        // given
        sut.round = 3
        sut.currentRecord = 2
        sut.bestRecord = 5
        
        // when
        sut.resetStage()
        
        // then
        XCTAssertEqual(
            sut.round,
            1,
            "Round should be reset to 1"
        )
        XCTAssertEqual(
            sut.currentRecord,
            0,
            "Current record should be reset to 0"
        )
        XCTAssertEqual(
            sut.bestRecord,
            0,
            "Best record should be reset to 0"
        )

    }
  • test_스테이지_초기화() 함수는 FindNumberModelresetStage 메서드가 제대로 작동하는지 테스트하는 Unit Test입니다. 해당 테스트는 Given-When-Then 형식으로 구성됩니다.
  • 테스트를 Given-When-Then 형식으로 설계하는 것이 좋은 관행입니다.
  1. Given(준비 단계)
    • 이 단계에서는 테스트에 필요한 초기 조건을 설정합니다.
    • 위 코드에서는 sut (System Under Test, 테스트 대상 시스템)의 round, currentRecord, bestRecord 값을 임의의 값으로 설정 합니다.
  2. When(실행 단계)
    • 테스트할 코드를 실행합니다. 여기서는 sut.resetStage()를 호출합니다.
    • 이 호출은 round, currentRecord,bestRecord 값을 초기화하는 것으로 기대됩니다.
  3. Then(검증 단계)
    • 실행한 코드의 결과를 검증합니다.
    • XCTAssertEqual을 사용하여 sutround,currentRecord,bestRecord 값이 각각 1,0,0 으로 재설정되었는지 확인합니다.
    • 만약 테스트가 실패한다면, "Round should be reset to 1", "Current record should be reset to 0", "Best record should be reset to 0" 등의 메시지가 출력됩니다.
  • 테스트 메서드 이름은 항상 test로 시작되고, 테스트하는 내용을 설명합니다.
  • 이 테스트를 실행하려면 Xcode의 테스트 네비게이터에서 해당 테스트 옆의 다이아몬드 아이콘을 클릭합니다. 이렇게 하면 앱이 빌드되고 실행되며, 아이콘이 녹색 체크 표시로 변경됩니다.
  • 테스트가 성공했다는 것을 나타내는 팝업도 Xcode 위에 나타납니다.

두번째 테스트 작성하기 - 기능 테스트 2

  • 두번째로는 타겟과 사용자의 선택 번호에 따라 점수(score), 단계(stage) 변화가 제대로 작동하는지 테스트 해보겠습니다.
  • 이 테스트는 두 가지 상황으로 나누어 진행할 예정입니다. 하나는 타겟 값과 사용자의 선택 값이 같을 때, 다른 하나는 두 값이 다를 때입니다.
  1. 타겟 값과 사용자의 선택 값이 같을 때
    • Given (준비 단계): sut의 타겟 값을 1로 설정하고, 예상되는 값(expectedValue)도 1로 설정합니다.
    • When (실행 단계): sut.checkNum(value: expectedValue)를 호출하여, 사용자의 선택 값이 타겟 값과 동일한 경우를 시뮬레이션합니다.
    • Then (검증 단계): sutcurrentRecord, bestRecord, round가 각각 1, 1, 2로 증가했는지 확인합니다. 이는 사용자의 선택이 정확했을 때의 기대 결과입니다.
    func test_사용자_선택번호와_타겟이_같을_경우() {
        // given
        sut.target = 1
        let expectedValue = sut.target
        
        // when
        sut.checkNum(value: expectedValue)
        
        // then
        XCTAssertEqual(sut.currentRecord, 1, "Current record should be incremented")
        XCTAssertEqual(sut.bestRecord, 1, "Best record should be incremented")
        XCTAssertEqual(sut.round, 2, "Rount should be incremented")
    }

  1. 타겟 값과 사용자의 선택 값이 다를 때
    • iven (준비 단계): sut의 타겟 값을 2로 설정하고, 사용자의 선택 값(expectedValue)을 1로 설정합니다.
    • When (실행 단계): sut.checkNum(value: expectedValue)를 호출하여, 사용자의 선택 값이 타겟 값과 다른 경우를 시뮬레이션합니다.
    • Then (검증 단계): sutcurrentRecord가 0으로 초기화되고, round가 1로 초기화된 것을 확인합니다. 이는 사용자의 선택이 틀렸을 때의 기대 결과입니다.
  func test_사용자_선택번호와_타겟이_다를_경우() {
        // given
        sut.target = 2
        let expectedValue = 1
        
        // when
        sut.checkNum(value: expectedValue)
        
        // then
        XCTAssertEqual(sut.currentRecord, 0, "Current record should be reset to 0")
        XCTAssertEqual(sut.round, 1, "Round should be reset to 1")
    }

UserDefaults 의존성으로 테스트 Repeatable 원칙 위배

  • 이 테스트 코드의 FindNumberTest 코드를 자세히 보면 UserDefaults를 사용하여 round, currentRecord, bestRecord 값을 저장하고 불러오고 있습니다. 이는 해당 UserDefaults를 그대로 활용하게 되면 테스트 간 상태 공유 문제, 테스트의 결과값이 동일하지 않은 문제가 발생할 수 있습니다.
  • 특히 타겟 값과 사용자의 선택 값이 같을 경우를 두 번 실행하면 실패가 발생할 가능성이 있습니다. 이는 UserDefaults에 저장된 값이 첫 번째 테스트 실행 후 변경되기 때문입니다.
  • 테스트 코드를 확인해보면 테스트 자체가 최초 첫번째 게임이 진행되는것을 가정함으로 저장된 UserDefaults에 값을 불러오는 상태로는 실패가 될 가능성이 있습니다.
  • 이를 해결하기 위해서는 Mock 객체를 사용 해야 합니다. Mock 객체를 사용하기 위해서는 사전 작업이 필요합니다.
  1. 의존성 주입 구현
    • FindNumberModelUserDefaults 인스턴스를 외부에서 주입할 수 있게 코드를 리팩토링 합니다. 이를 통해 실제 앱에서는 기본 UserDefaults를, 테스트에서는 MockUserDefaluts를 사용할 수 있습니다.
import Foundation

final class FindNumberModel {
    
    var urlSession: URLSessionProtocol = URLSession.shared
    var defaults: UserDefaults = UserDefaults.standard
    
    var round: Int {
        get {
            let initialRound = defaults.integer(forKey: "Round")
            return initialRound == 0 ? 1 : initialRound
        }
        set {
            defaults.set(newValue, forKey: "Round")
        }
    }
    var bestRecord: Int {
        get {
            defaults.integer(forKey: "bestRecord")
        }
        set {
            defaults.set(newValue, forKey: "bestRecord")
        }
    }
    
    var currentRecord: Int {
        get {
            defaults.integer(forKey: "CurrentRecord")
        }
        set {
            defaults.set(newValue, forKey: "CurrentRecord")
        }
    }
    
    var onUpdate: (() -> Void)?
    var onError: ((String) -> Void)?

    var target: Int = 1
    
    init() {
        gameStart()
    }    
  • 위 코드에서 urlSession, defaults를 외부에서 의존성을 주입하도록 코드를 리팩토링 해보겠습니다.
  • 여기서 URLSessionProtocol은 실제 사용할 URLSession과 테스트 코드에서 실행할 URLSessionStub 을 위한 protocol입니다.
  • 즉, URLSessionProtocolURLSession의 기능을 모방하도록 설계된 protocol입니다. 해당 protocol을 사용함으로써, 실제 네트워크 통신을 수행하는 URLSession과 테스트를 위한 가짜 네트워크 통신을 처리하는 URLSessionStub 사이의 교체가 용이해집니다.
  • URLSessionStub은 네트워크 요청에 대한 응답을 모방하여 테스트 중에 실제 네트워크 호출 없이도 네트워크 통신의 결과를 테스트할 수 있게 해줍니다.
var urlSession: URLSessionProtocol!
var defaults: UserDefaults!
		
init( 
	urlSession: URLSessionProtocol = URLSession.shared, 
	defaults: UserDefaults = UserDefaults.standard
) { 
	self.urlSession = urlSession
	self.defaults = defaults
	
	gameStart()
}
  • 기존 class 내부에서 설정한 변수를 외부에서 주입할 수 있도록 변경합니다.
  1. Mock 객체 사용

    • FindNumberModelUserDefaults 의존성을 제거하고, 테스트에서 MockUserDefaults 객체를 사용하도록 합니다. 이렇게 하면 테스트 각각이 격리된 환경에서 실행되고, 실제 앱의 UserDefaults와 독립적으로 작동합니다.
    • MockUserDefaults 클래스를 생성하며 해당 클래스는 FindNumberTests.swift 파일에 생성합니다.
    final class MockUserDefaults: UserDefaults { 
    	private var storage = [String: Any]() 
    	
    	override func integer(forKey defaultName: String) -> Int {
        return storage[defaultName] as? Int ?? 0
        }
    
        override func set(_ value: Int, forKey defaultName: String) {
        storage[defaultName] = value
        }
    }
    • 또한 이렇게 만들어진 MockUserDefaults와 기존에 만들어둔 URLSessionStubsetupWithError()메서드에서 sut 이 초기화 되는 시점에 주입 시켜줍니다.
    override func setUpWithError() throws {
        try super.setUpWithError()
        
        let urlSessionStub = URLSessionStub()
        let mockUserDefaults = MockUserDefaults(suiteName: "TestDefaults")
    
        sut = FindNumberModel(
                urlSession: urlSessionStub,
                defaults: mockUserDefaults!
        )
    }
    • 이러한 리팩토링을 통해 FindNumberModelresetStage 메서드 및 다른 메서드들을 실제 UserDefalutsURLSession의 영향 없이 테스트할 수 있으며, 테스트의 격리성과 신뢰성을 높일 수 있습니다.

세번째 테스트 작성하기 - 기능 테스트 3

  • 세번째로는 FindNumberModelgetNumber 메서드가 API 호출을 통해 올바른 타겟 번호를 받아오는지 테스트 합니다. 이 테스트는 주로 네트워크 요청을 모방하고, 응답을 검증하는 과정을 포함합니다.
  • 이 테스트의 핵심은 실제 네트워크 요청 없이 API 응답을 모방하고, FindNumberModel이 응답을 올바르게 처리하는지 확인하는 것 입니다. URLSessionStub을 사용함으로써, 네트워크 상태나 외부 API 서버의 상태에 영향을 받지 않고 일관된 테스트 환경을 유지할 수 있습니다.
    func test_타겟번호_받기() {
        // given
        let stubbedData = "[1]".data(using: .utf8)
        let urlString = "http://www.randomnumberapi.com/api/v1.0/random?min=1&max=3&count=1"
        let url = URL(string: urlString)!
        let stubbedResponse = HTTPURLResponse(
            url: url,
            statusCode: 200,
            httpVersion: nil,
            headerFields: nil
        )
        let urlSessionStub = URLSessionStub(
            stubbedData: stubbedData,
            stubbedResponse: stubbedResponse,
            stubbedError: nil
        )
        
        sut.urlSession = urlSessionStub
        
        // when
        var receivedNumber: Int?
        sut.getNumber { number in
            receivedNumber = number
        }
        
        // then
        XCTAssertEqual(receivedNumber, 1)
    }
  1. Given (준비 단계)
    • stubbedData: 예상되는 API 응답 데이터를 모방합니다. 여기서는 "[1]" 문자열을 데이터 형태로 변환합니다.
    • urlString, url, stubbedResponse: 테스트할 API의 URL과 해당 요청에 대한 가짜 HTTP 응답을 설정합니다.
    • urlSessionStub: 실제 네트워크 통신 대신 사용될 URLSessionStub 인스턴스를 생성합니다. 이 객체는 네트워크 요청을 모방하며, stubbedDatastubbedResponse를 사용하여 예상되는 응답을 반환합니다.
  2. When (실행 단계)
    • sut.getNumber: FindNumberModelgetNumber 메서드를 호출합니다. 이 메서드는 내부적으로 urlSessionStub을 사용하여 네트워크 요청을 모방합니다.
    • receivedNumber: getNumber 메서드의 완료 핸들러에서 반환된 타겟 번호를 저장합니다. 이 값은 비동기적으로 설정되므로, 테스트가 이 값을 올바르게 수신하였는지 확인하는 것이 중요합니다.
  3. Then (검증 단계)
    • XCTAssertEqual: receivedNumber가 예상되는 값인 1과 동일한지 확인합니다. 이는 getNumber 메서드가 API 응답을 올바르게 처리하고, 정확한 타겟 번호를 반환하는지를 검증합니다.
  • 이러한 테스트를 하는 주된 이유는 아래와 같습니다.
  1. 네트워크 의존성 제거
    • 실제 네트워크 호출을 하지 않음으로써 테스트의 안정성을 보장합니다. 실제 네트워크에 의존하게 되면, 네트워크 상태나 외부 서버의 문제로 테스트가 실패할 수 있습니다.
  2. 속도와 효율성
    • 실제 네트워크 호출은 시간이 많이 소요될 수 있습니다. API를 모방하면 테스트 실행 시간을 크게 단축시킬 수 있습니다.
  3. 제어된 테스트 조건
    • 테스트에서는 다양한 시나리오(성공적인 응답, 오류 응답 등)를 모방하여 테스트할 수 있어야 합니다. 실제 API를 사용하면 이러한 조건을 완벽히 제어하기 어렵습니다.
  4. 비용 절감
    • 실제 API를 사용할 경우 비용이 발생할 수 있습니다. 모방을 사용하면 불필요한 비용을 줄일 수 있습니다.
  5. 보안
    • 실제 서버에 접근하지 않기 때문에, 보안상의 이슈나 데이터 유출의 위험을 줄일 수 있습니다.
  6. 개발과 테스트의 분리
    • 백엔드가 완전히 개발되지 않았거나 접근할 수 없는 상태에서도 프론트엔드의 로직을 테스트할 수 있습니다.

Slow Test

  • FindNumberSlowTests라는 target을 새롭게 생성합니다. 이렇게 만들어진 클래스는 실제 네트워크 연결을 사용하여 API 호출을 테스트하는 슬로우 테스트(slow test) 케이스 입니다.
  • 이 테스트는 실제 네트워크 요청을 수행하고, 그 결과를 검증합니다.
import XCTest

@testable import FindNumber

final class FindNumberSlowTests: XCTestCase {
    var sut: URLSession!
    let networkMonitor = NetworkMonitor.shared

    override func setUpWithError() throws {
        try super.setUpWithError()
        sut = URLSession(configuration: .default)
    }
    
    override func tearDownWithError() throws {
        sut = nil
        try super.tearDownWithError()
    }
    
    func test_유효한API_호출후_HTTPStatusCode200_받기() throws {
        try XCTSkipUnless(networkMonitor.isReachable, "Network connectivity needed for this test.")

        // given
        let urlString = "https://www.randomnumberapi.com/api/v1.0/random?min=0&max=3&count=1"
        let url = URL(string: urlString)!
        let promise = expectation(description: "Completion handler invoked")
        var statusCode: Int?
        var responseError: Error?
        
        // when
        let dataTask = sut.dataTask(with: url) { _, response, error in
            statusCode = (response as? HTTPURLResponse)?.statusCode
            responseError = error
            promise.fulfill()
        }
        
        dataTask.resume()
        wait(for: [promise], timeout: 3)
        
        // then
        XCTAssertNil(responseError)
        XCTAssertEqual(statusCode, 200)
    }
}
  1. Given (준비 단계): 유효한 API URL을 설정하고, 비동기적 완료를 기다리는 프라미스를 생성합니다.
  2. When (실행 단계): 실제 URLSession을 사용하여 API에 HTTP GET 요청을 보내고, 결과를 비동기적으로 받습니다. 이 때 XCTSkipUnless를 사용하여 네트워크 연결이 없는 경우 테스트를 건너뛸 수 있습니다.
  3. Then (검증 단계): API 호출이 성공적으로 완료되었는지 (responseErrornil인지), 그리고 HTTP 상태 코드가 200인지 검증합니다. 테스트는 네트워크 상태에 의존하므로 완료까지 몇 초가 걸릴 수 있습니다.
  • 이 테스트는 실제 네트워크 상황에서 API가 올바르게 작동하는지 확인하는 데 유용하지만, 네트워크 의존성과 시간 지연으로 인해 일반적인 유닛 테스트보다 느립니다. 그렇기 때문에 이러한 종류의 테스트는 필요한 경우에만 선택적으로 사용하는 것이 좋습니다.

UI Test

  • UI Test를 통해 사용자 인터페이스와의 상호작용을 테스트할 수 있습니다. UI Test는 앱의 UI 객체를 쿼리를 사용하여 찾고, 이벤트를 동기화 합니다. 그런다음 해당 이벤트를 해당 객체에 전송하는 방식으로 작동합니다.
  • FindNumber 프로젝트의 테스트 네비게이터에서, UI Test Target을 추가합니다.
  • 그리고 해당 프로버티를 FindNumberUITest class 내부 최상단에 기입 해줍니다.
var app: XCUIApplication!
  • tearDownWithError()를 제거하주고, setUpWithError() 메서드의 내용을 아래와 같이 변경해줍니다.
try super.setupWithError()
continueAfterFailure = false
app = XCUIApplication()
app.launch()
  • 기존에 있던 example 코드를 삭제하고 testStageAscent() 메서드를 추가해줍니다.
  • 해당 메서드는 번호 버튼을 눌러 한 번이라도 성공했을 때 최대 성공 값이 0이 아닌지 확인하는 테스트입니다.
    func testStageAscent() {
        // given
        let button = app.buttons["1번"]
        let button2 = app.buttons["2번"]
        let reset = app.buttons["다시 시작"]
        
        reset.tap()
        button.tap()
        button2.tap()
        button.tap()
        button2.tap()
        button.tap()
        button2.tap()
        button.tap()
        button2.tap()
        
        // then
        let stageLabel = app.staticTexts["최대: 0번 연속 성공!"]
        
        XCTAssertFalse(stageLabel.exists,  "Stage label should be updated to the correct stage number")
    }
  • 해당 테스트는 사용자가 번호 버튼을 눌러 게임을 진행하는 시나리오를 시뮬레이션합니다.
  • 이 과정에서 앱이 사용자의 입력을 받아들이고, 게임의 상태를 적절히 변경하는지 검사합니다.

출처(참고문헌)

원본 코드

제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.

감사합니다.

0개의 댓글

관련 채용 정보