iOS. UI Test 자동화

iOS 초보 2020. 4. 17. 11:47

* UI Test 용 target 을 추가한다.

 

* 적당한 폴더를 만들고, UI Test Case Class 템플릿 파일을 만든다.

 

* 이제 UI Test 자동화 코딩을 한다.

import XCTest

class UITests: XCTestCase {

    /**
     * 테스트 함수가 실행되기 전에 항상 실행됨.
     * 여기서 초기화 작업을 해라.
     */
    override func setUpWithError() throws {
        continueAfterFailure = false
    }

    /**
     * 테스트 함수가 실행끝난후 항상 실행됨.
     * 여기서 정리 작업을 해라.
     */
    override func tearDownWithError() throws {

    }

    /**
     * 이건 성능을 측정할때 사용하는거라고 함.
     */
    func testLaunchPerformance() throws {
        if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
            // This measures how long it takes to launch your application.
            measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) {
                XCUIApplication().launch()
            }
        }
    }
    
    /**
     * 설정화면 UI Test
     */
    func testSettingVC() throws
    {
        // 비동기 api호출이 끝나기를 기다릴 시간.
        let timeout:TimeInterval = 5
        
        // 앱 생성, 실행
        let app = XCUIApplication()
        app.launch()

        // 웹뷰에서 설정화면(native화면)으로 들어가기
        let textViewsQuery = app.webViews.webViews.textViews
        XCTAssertTrue(textViewsQuery.staticTexts["더보기"].waitForExistence(timeout: timeout))
        textViewsQuery.staticTexts["더보기"].tap()
        XCTAssertTrue(textViewsQuery.otherElements["banner"].children(matching: .button).element.waitForExistence(timeout: timeout))
        textViewsQuery.otherElements["banner"].children(matching: .button).element.tap()

        // 설정화면에 들어가면 바로 api를 호출하고 결과를 받은후, 테이블뷰에 데이타를 보여준다.
        // 따라서, 테이블뷰가 존재할때까지 기다린다.
        XCTAssertTrue(app.tables.firstMatch.waitForExistence(timeout: timeout))
        let tablesQuery = app.tables

        // 테이블뷰에서 첫번째 셀을 찾고, 그 안에 있는 스위치버튼을 on/off 한번씩 한다.
        var cellsQuery = tablesQuery.cells.containing(.staticText, identifier:"푸시 알림 받기")
        if !cellsQuery.buttons["switch on"].exists
        {
            cellsQuery.buttons["switch off"].tap()
        }
        cellsQuery.buttons["switch on"].tap() // on 상태를 탭하니까, off 상태로 바뀌는 것임.
        cellsQuery.buttons["switch off"].tap() // off 상태를 탭하니까, on 상태로 바뀌는 것임.
        
        // 테이블뷰에서 두번째 셀을 찾는다.
        // 첫번째 셀을 on 해야 두번째 셀이 생긴다,
        // 바로 위에서 첫번째 셀을 on 했으니까, api호출하고 응답을 받은후, 테이블뷰를 새로 그려서
        // 두번째 셀이 생길때까지 기다린다.
        cellsQuery = tablesQuery.cells.containing(.staticText, identifier:"22222222222")
        XCTAssertTrue(cellsQuery.buttons["switch on"].waitForExistence(timeout: timeout) || cellsQuery.buttons["switch off"].waitForExistence(timeout: timeout))
        if !cellsQuery.buttons["switch on"].exists
        {
            cellsQuery.buttons["switch off"].tap()
        }
        cellsQuery.buttons["switch on"].tap()
        cellsQuery.buttons["switch off"].tap()

        // 테이블뷰에서 세번째 셀을 찾는다.
        // 이 안에는 몇개의 버튼이 더 있어서, 그것들도 한번씩 탭해본다.
        cellsQuery = tablesQuery.cells.containing(.staticText, identifier:"3333333333")
        XCTAssertTrue(cellsQuery.buttons["switch on"].waitForExistence(timeout: timeout) || cellsQuery.buttons["switch off"].waitForExistence(timeout: timeout))
        if !cellsQuery.buttons["switch on"].exists
        {
            cellsQuery.buttons["switch off"].tap()
        }
        XCTAssertTrue(cellsQuery.children(matching: .button).element(boundBy: 1).waitForExistence(timeout: timeout))
        cellsQuery.children(matching: .button).element(boundBy: 1).tap()
        XCTAssertTrue(cellsQuery.children(matching: .button).element(boundBy: 0).waitForExistence(timeout: timeout))
        cellsQuery.children(matching: .button).element(boundBy: 0).tap()
        XCTAssertTrue(cellsQuery.buttons["switch on"].waitForExistence(timeout: timeout))
        cellsQuery.buttons["switch on"].tap()
        XCTAssertTrue(cellsQuery.buttons["switch off"].waitForExistence(timeout: timeout))
        cellsQuery.buttons["switch off"].tap()

        ...
        
        // 중간중간 sleep 을 사용하여 잠시 기다릴수 있음 (단위 초)
        sleep(1)

        // 화면을 스크롤 업.
        app.swipeUp()
        
        // 현재시간에서 minutes 값을 가져옴.
        let formatter = DateFormatter()
        formatter.dateFormat = "mm"
        let minutes = formatter.string(from: Date())
        
        // 셀안에 있는 time picker에 현재시간의 분 값을 설정함.
        cellsQuery = tablesQuery.cells.containing(.staticText, identifier:"시간 설정")
        XCTAssertTrue(cellsQuery.buttons["switch on"].waitForExistence(timeout: timeout) || cellsQuery.buttons["switch off"].waitForExistence(timeout: timeout))
        if !cellsQuery.buttons["switch on"].exists
        {
            cellsQuery.buttons["switch off"].tap()
        }
        XCTAssertTrue(tablesQuery.cells.containing(.staticText, identifier:"시작 시간").children(matching: .button).element.waitForExistence(timeout: timeout))
        tablesQuery.cells.containing(.staticText, identifier:"시작 시간").children(matching: .button).element.tap()
        sleep(1)
        var datePickersQuery = app.datePickers
        datePickersQuery.pickerWheels.element(boundBy: 1).adjust(toPickerWheelValue: minutes)
        app.staticTexts["선택"].tap()
        
        ...
    }
}

 

* 코드를 보면, 화면에 있는 ui 요소들을 다 테스트하기 위해 코드양이 상당히 많은데
이걸 다 손으로 넣은건 아니다.
시뮬에이터에서 앱을 실행시킨후, 녹음 버튼을 누르고, 테스트 하기 원하는 작업을 직접 하면 자동으로 코드가 작성된다.
물론 완벽하지 않아서, 수정을 많이 해야 하지만,
처음에 틀을 잡아줘서 편하다.

 

* 코딩이 끝난후, 아래 이미지처럼 함수명 왼쪽에 있는 플레이 버튼을 누르면,
앱이 실행되고 자동으로 ui 요소들을 테스트한다.
처음해보는거라서 작성할때 시간이 많이 소비되었지만,
앞으로 매번 손으로 테스트하는 일이 없어져서 큰 도움이 될것이다.

 

반응형
Posted by 돌비
,