UITest) Accessibility

SteadySlower·2023년 6월 13일
0

iOS Development

목록 보기
20/38
post-custom-banner

이 포스팅은 사내에서 진행한 Tech Talk을 정리한 포스팅입니다.

이번에는 UITest와 밀접한 관련이 있는 Accessibility라는 개념을 알아보도록 하겠습니다.

Accessibility (접근성)

Accessibility란 고령자나 장애인처럼 기존의 방법으로는 앱 사용이 어려운 사용자가 앱을 사용할 수 있도록 해주는 앱의 특성을 이야기합니다.

iOS의 경우 Voice Over를 예시로 들 수 있는데요. Voice Over라는 기능을 켜게되면 화면을 보지 않고도 음성을 통해서 앱을 인식하고 사용할 수 있게 해줍니다.

개발자들은 이런 사용자들을 위해서 앱을 제작할 때 Accessibility를 감안하여 앱을 제작해야 합니다.

Accessibility Inspector

Xcode의 개발자 도구 중에는 Accessibility Inspector라는 도구가 있습니다. 이 도구를 통해서 나의 앱 (혹은 현재 실행 중인 앱)의 Accessibility를 알아볼 수 있습니다.

해당 어플을 사용하면 앱의 각각의 요소가 어떤 이름으로 음성으로 표현되는지 들어볼 수 있습니다.

Accessibility Identifier vs Accessibility Label

Accessibility와 관련해서 우리는 UI 객체에 위 2가지 속성을 부여할 수 있는데요. 각각 어떤 특성을 가지고 있는지 보여드리겠습니다.

Accessibility Identifier

Accessibility Identifier는 아래 설명에서도 볼 수 있듯이 testing을 위한 값입니다. UITest를 개발할 때 해당 객체를 인식하기 위해서 사용합니다. Accessibility Identifier를 부여한 UI객체는 Test 코드 내에서 해당 이름으로 인식할 수 있습니다.

Accessibility Label

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)
        
    }
}
profile
백과사전 보다 항해일지(혹은 표류일지)를 지향합니다.
post-custom-banner

0개의 댓글