이 포스팅은 사내에서 진행한 Tech Talk을 정리한 포스팅입니다.
이번에는 UITest와 밀접한 관련이 있는 Accessibility라는 개념을 알아보도록 하겠습니다.
Accessibility란 고령자나 장애인처럼 기존의 방법으로는 앱 사용이 어려운 사용자가 앱을 사용할 수 있도록 해주는 앱의 특성을 이야기합니다.
iOS의 경우 Voice Over를 예시로 들 수 있는데요. Voice Over라는 기능을 켜게되면 화면을 보지 않고도 음성을 통해서 앱을 인식하고 사용할 수 있게 해줍니다.
개발자들은 이런 사용자들을 위해서 앱을 제작할 때 Accessibility를 감안하여 앱을 제작해야 합니다.
Xcode의 개발자 도구 중에는 Accessibility Inspector라는 도구가 있습니다. 이 도구를 통해서 나의 앱 (혹은 현재 실행 중인 앱)의 Accessibility를 알아볼 수 있습니다.
해당 어플을 사용하면 앱의 각각의 요소가 어떤 이름으로 음성으로 표현되는지 들어볼 수 있습니다.
Accessibility와 관련해서 우리는 UI 객체에 위 2가지 속성을 부여할 수 있는데요. 각각 어떤 특성을 가지고 있는지 보여드리겠습니다.
Accessibility Identifier는 아래 설명에서도 볼 수 있듯이 testing을 위한 값입니다. UITest를 개발할 때 해당 객체를 인식하기 위해서 사용합니다. Accessibility Identifier를 부여한 UI객체는 Test 코드 내에서 해당 이름으로 인식할 수 있습니다.
Accessibility Identifier가 개발자를 위한 속성이라면 Accessibility Label는 사용자를 위한 속성입니다. 쉽게 말하면 Voice Over를 실행했을 때 읽어주는 음성이라고 보면 됩니다.
사용자를 위한 것이므로 사용자가 한국인이라면 한국어 String을 사용해야 합니다. 따라서 보통 LocalizedString으로 구현하는 것이 권장됩니다.
UITest를 설명하는데 사용했던 이름 입력 View의 각 component에 Accessibility Identifier와 Accessibility Label을 적용한 것입니다.
예를 들어 이름을 입력하는 TextField의 경우에는 Identifier로 “name field”를 적용했습니다. 이 이름은 UITest에서 활용할 수 있습니다.
반면에 label은 “이름 텍스트 필드”를 적용했습니다. 한국인이 대상인 앱이기 때문에 한국어 String을 사용한 모습입니다. Accessibility Inspector를 사용해보면 해당 View를 “이름 텍스트 필드”라고 읽어주는 것을 확인할 수 있습니다.
import SwiftUI
struct NameView: View {
@State var input: String = ""
@Binding var name: String
@State var showAlert = false
var body: some View {
VStack {
TextField("이름", text: $input)
.frame(width: 100, height: 50)
.border(.black)
.accessibilityIdentifier("name field")
.accessibilityLabel("이름 텍스트 필드")
Button {
if isNameValid() {
name = input
} else {
showAlert = true
}
} label: {
Text("입력")
.frame(width: 100, height: 50)
.foregroundColor(.white)
.background(.mint)
.cornerRadius(25)
}
.accessibilityIdentifier("input button")
.accessibilityLabel("입력버튼")
}
.alert("이름은 5글자 이상으로 해주세요", isPresented: $showAlert) {
Button("다시 입력") { input = "" }
.accessibilityIdentifier("alert button")
.accessibilityLabel("팝업 종료 버튼")
}
}
func isNameValid() -> Bool {
return input.count < 5 ? false : true
}
}
위 View를 테스트 하기 위한 테스트 코드입니다. 지난 번 포스팅까지는 Xcode가 자동으로 생성해준 이름을 사용했는데요. 이제는 해당 View를 Accessibility Identifier를 통해서 정의할 수 있습니다.
import XCTest
final class UITests_Name_Accessibility: XCTestCase {
let app = XCUIApplication()
override func setUpWithError() throws {
continueAfterFailure = false
app.launch()
}
func inputName(_ name: String) {
let textField = app.textFields["name field"] //👉 Accessibility Identifier
textField.tap()
let isKeyboardEngish = app.keyboards.keys["A"].exists
if !isKeyboardEngish {
app.buttons["Next keyboard"].tap()
}
for char in name {
let key = app.keyboards.keys["\(char)"]
key.tap()
}
let inputButton = app.buttons["input button"] //👉 Accessibility Identifier
inputButton.tap()
}
func test_NameView_inputButton_success() {
let name = "Teddy"
inputName(name)
let welcomeMessage = app.staticTexts["Let's Quiz, \(name)!"]
XCTAssert(welcomeMessage.exists)
}
func test_NameView_inputButton_failure() {
let name = "Tom"
inputName(name)
let isAlertExist = app.alerts.firstMatch.waitForExistence(timeout: 5)
XCTAssert(isAlertExist)
}
func test_NameView_inputButton_retry() {
let invalidName = "Tom"
inputName(invalidName)
let alert = app.alerts.firstMatch
let alertButton = alert.buttons["alert button"] //👉 Accessibility Identifier
alertButton.tap()
let validName = "Teddy"
inputName(validName)
let welcomeMessage = app.staticTexts["welcome message label of \(validName)"]
XCTAssert(welcomeMessage.exists)
}
}