UITest
참고자료
https://www.kodeco.com/21020457-ios-unit-testing-and-ui-testing-tutorial?page=3
참고자료를 토대로 공부하고 상세히 기록합니다.
Xcode의 UI Test
UITest를 통해 사용자 인터페이스와 상호작용을 테스트할 수 있습니다.
UI테스트는 쿼리를 사용하여 앱의 UI객체를 찾고, 이벤트를 합성한 다음 해당 객체에 이벤트를 보내는 방식으로 작동합니다.
API를 사용하면 UI객체의 속성과 상태를 검사하여 예상 상태와 비교할 수 있습니다.
Test navigator에서 새 UI Test Target을 추가합니다.
Test Target이 BullsEye인지 확인하고 기본 이름은 BullsEyeUITest를 사용합니다.
UnitTest와 마찬가지로 클래스가 생성됩니다. 하나씩 알아봅시다.
import XCTest // XCTest 프레임워크를 가져옵니다.
final class BullsEyeUITests: XCTestCase { // XCTestCase를 상속받아 BullsEyeUITests라는 이름의 테스트 케이스 클래스를 정의합니다.
var app: XCUIApplication!
// 각 테스트 메서드가 실행되기 전에 호출되는 메서드입니다.
override func setUpWithError() throws {
continueAfterFailure = false // 테스트가 실패한 후에도 다음 테스트를 계속 진행할지를 결정합니다. false로 설정하면 테스트 실패 시 해당 테스트 케이스의 나머지 테스트는 모두 중단됩니다.
app = XCUIApplication() // 앱의 UI 테스트를 위한 객체를 생성합니다.
app.launch() // 앱을 실행합니다.
}
// 각 테스트 메서드가 종료된 후에 호출되는 메서드입니다.
override func tearDownWithError() throws {
}
// 예제 테스트 메서드입니다. 실제로 아무런 기능을 수행하지 않는 placeholder 메서드입니다.
func testExample() throws {
}
// 앱의 실행 성능을 측정하는 테스트 메서드입니다.
func testLaunchPerformance() throws {
// macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0 이상에서만 해당 코드를 실행합니다.
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
// 앱의 시작 성능을 측정합니다.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
}
이제 test를 배워봅시다.
새로운 test를 하나 추가해줍니다.
func testGameStyleSwitch() {
}
그리고, testGameStyleSwitch()에서 새 줄을 열고(커서를 위치하고) 편집기 하단의 빨간색 녹화버튼을 클릭해줍니다.
이렇게 하면 상호작용을 테스트 명령으로 기록하는 모드에서 시뮬레이터에서 앱을 엽니다.
앱이 로드되면 우측 하단의 게임 스타일 스위치의 slide세그먼트와 top label을 누릅니다.
그리고, XCode의 녹화버튼을 다시 클릭해 녹화를 중지합니다.
그럼 testGameStyleSwitch()에 다음 세 줄이 추가됩니다.
Recoder는 앱에서 테스트한 동일 작업을 테스트하기 위한 코드를 생성합니다. 게임 스타일 세그먼트 컨트롤과 상단 레이블에 탭을 보냅니다.
이를 기반으로 자체 UI테스트를 생성합니다.
첫 번째 줄은 setUpWithError()에서 생성한 속성을 복제한 것이므로 해당 행을 삭제합니다.
아직 탭할 필요가 없으므로
2, 3행의 끝에있는 .tap()도 삭제합니다.
이제 ["Slide"] 옆의 작은 메뉴를 클릭해서 segmentedControls.buttons["Slide"]를 선택합니다.
남아있어야 할 코드는 다음과 같습니다.
app.segmentedControls.buttons["Slide"]
app.staticTexts["Get as close as you can to: "]
이제 Recoder가 테스트에서 액세스할 수 있는 코드를 찾도록 도움이 되도록 다른 object를 탭합니다.
그 다음 이러한 라인을 이 코드로 교체하여 주어진 섹션을 생성합니다.
// given
let slideButton = app.segmentedControls.buttons["Slide"]
let typeButton = app.segmentedControls.buttons["Type"]
let slideLabel = app.staticTexts["Get as close as you can to: "]
let typeLabel = app.staticTexts["Guess where the slider is: "]
이제 segmented control에 있는 두 버튼과 두개의 상단 레이블에 대한 이름이 지정되었으므로 다음 코드를 추가해줍니다.
// then
if slideButton.isSelected {
XCTAssertTrue(slideLabel.exists)
XCTAssertFalse(typeLabel.exists)
typeButton.tap()
XCTAssertTrue(typeLabel.exists)
XCTAssertFalse(slideLabel.exists)
} else if typeButton.isSelected {
XCTAssertTrue(typeLabel.exists)
XCTAssertFalse(slideLabel.exists)
slideButton.tap()
XCTAssertTrue(slideLabel.exists)
XCTAssertFalse(typeLabel.exists)
}
하나 씩 알아봅시다.
XCTAssertTrue는 파라미터가 false로 들어왔을 때 테스트가 실패하게됩니다.
XCTAssertFalse는 파라미터가 true로 들어왔을 때 테스트가 실패하게 됩니다.
Label에서 exists속성이 자주 쓰이는 것을 확인할 수 있습니다.
exists는 XCUIElement클래스의 프로퍼티 중 하나입니다. 이것은 버튼, 라벨, 텍스트필드 등 다양한 UI컴포넌트에 해당할 수 있습니다.
exists프로퍼티는 해당 UI요소가 현재 화면에 실제로 존재하는지, 앱의 액세스 가능한 계층 구조 내에 있는지를 나타냅니다.
true: 해당 요소가 액세스 가능한 상태이며 현재 화면에 나타나 있습니다.
false: 해당 요소가 액세스 불가능 하거나, 현재 화면에 나타나 있지 않습니다.
이제 좀 이해가 됩니다.
테스트를 진행하면 테스트는 성공하게 되고 True, False를 바꿔보면 실패하게 되는것을 확인할 수 잇습니다.
Code Coverage를 활성화
code coverage tool은 테스트가 실재로 실행중인 앱 코드를 알려줍니다.
따라서 앱에서 어떤 부분이 테스트되지 않았는지 알 수 있습니다.
code coverage를 활성화 하려면 스키마의 테스트 작업을 편집하고 옵션 탭에서 Gather coverage for 체크박스를 선택합니다.
Scheme 창이 열리면 좌측 탭에서 Test로 이동
창이 열리면 "Coe Coverage"를 찾아 설정해줍니다.
설정이 끝난 후 Command-U를 사용하여 모든 테스트를 실행한 후 Command-9를 사용하여 report navigator를 엽니다.
목록의 맨 위 항목의 커버리지를 선택합니다.
각 .swift파일에 커서를 올리면 화살표 버튼이 나타나며 각 파일로 이동하게됩니다.
오른쪽 사이드바의 커버리지 주석을 마우스로 가리키면 코드의 섹션이 녹색 또는 빨간색으로 강조 표시 됩니다.
호출되지 않은(테스트 되지 않은) 섹션은 빨간색으로 강조 표시 됩니다.
100% 커버리지를 달성해야할까요?
구글에 "100% unit test coverage"를 검색하면 이에 대한 찬반 논란과 "100%커버리지의 정의"에 대한 논쟁을 찾을 수 있습니다.
이에 대한 반대 의견은 마지막 10~15%는 노력 할 가치가 없다고 합니다. 그에 반해 찬성하는 의견은 마지막 10 ~ 15%가 테스트하기 어려워서 가장 중요하다고 말합니다. "테스트하기 어려운 코드가 나쁜 디자인"을 Google에 검색하면 테스트할 수 없는 코드가 더 깊은 디자인 문제의 징조라는 주장을 찾을 수 있습니다.