UI Testing a SwiftUI application in Xcode | Advanced Learning #18
edit Scheme
override func setUpWithError() throws {
continueAfterFailure = false
app.launchArguments = ["-UITest_startSignedIn"]
app.launch()
}
app.launchArguments
를 통해 특정 값을 전달 → UI 테스팅 단계의 각 테스트들이 UITest_startSignedIn
을 사용할 때 불필요한 크래시가 나지 않는지 확인 필요import SwiftUI
class UITestingBootCampViewModel: ObservableObject {
let placeholderText: String = "Add your name..."
@Published var textFieldText: String = ""
@Published var currentUserIsSignedIn: Bool
init(currentUserIsSignedIn: Bool) {
self.currentUserIsSignedIn = currentUserIsSignedIn
}
func signUpButtonPressed() {
guard !textFieldText.isEmpty else { return }
currentUserIsSignedIn = true
}
}
struct UITestingBootCampView: View {
@StateObject private var viewModel: UITestingBootCampViewModel
init(currentUserIsSignedIn: Bool) {
_viewModel = StateObject(wrappedValue: UITestingBootCampViewModel(currentUserIsSignedIn: currentUserIsSignedIn))
}
var body: some View {
ZStack {
LinearGradient(
gradient: Gradient(colors: [.blue, .cyan, .indigo]),
startPoint: .topLeading,
endPoint: .bottomTrailing)
.ignoresSafeArea()
ZStack {
if viewModel.currentUserIsSignedIn {
SignedInHomeView()
}
if !viewModel.currentUserIsSignedIn {
signUpLayer
.frame(maxWidth: .infinity, maxHeight: .infinity)
.transition(.move(edge: .leading))
}
}
}
}
}
extension UITestingBootCampView {
private var signUpLayer: some View {
VStack {
TextField(viewModel.placeholderText, text: $viewModel.textFieldText)
.accessibilityIdentifier("SignUpTextField")
.font(.headline)
.padding()
.background(.white)
.cornerRadius(10)
Button {
withAnimation(.spring()) {
viewModel.signUpButtonPressed()
}
} label: {
Text("Sign Up")
.accessibilityIdentifier("SignUpButton")
.font(.headline)
.withDefaultButtonFormmating(Color.pink.opacity(0.6))
.padding(.horizontal, 60)
}
.withPressableStyle(0.9)
}
.padding(.horizontal, 30)
}
}
struct SignedInHomeView: View {
@State private var showAlert: Bool = false
var body: some View {
NavigationView {
VStack(spacing: 20) {
Button {
showAlert.toggle()
} label: {
Text("Show Welcome Alert!")
}
.accessibilityIdentifier("ShowAlertButton")
.alert(isPresented: $showAlert) {
Alert(title: Text("WELCOME TO THE WORLD!"))
}
NavigationLink {
Text("DESTINATION")
.font(.headline)
.padding(.horizontal, 60)
} label: {
Text("NAVIGATION")
.font(.headline)
.withDefaultButtonFormmating(Color.pink.opacity(0.6))
.padding(.horizontal, 60)
}
.accessibilityIdentifier("NavigationLinkToDestination")
}
.padding()
.navigationTitle("WELCOME")
}
}
}
import SwiftUI
@main
struct SwiftfulThinkingAdvancedLearningApp: App {
let currentUserIsSignedIn: Bool
init() {
// First Initializer when app launched
let userIsSignedIn: Bool = CommandLine.arguments.contains("-UITest_startSignedIn") ? true : false
// let userIsSignedIn: Bool = ProcessInfo.processInfo.environment["-UITest_startSignedIn2"] == "true" ? true : false
self.currentUserIsSignedIn = userIsSignedIn
}
var body: some Scene {
WindowGroup {
UITestingBootCampView(currentUserIsSignedIn: currentUserIsSignedIn)
}
}
}
userIsSignedIn
이라는 환경변수를 앱 실행 이전 입력 가능extension UITestingBootCampView_UITests {
func signUpAndSignIn(shouldTypeOnKeyboard: Bool) {
let textField = app.textFields["SignUpTextField"]
textField.tap()
if shouldTypeOnKeyboard {
textField.typeText("ID")
}
let signUpButton = app.buttons["SignUpButton"]
signUpButton.tap()
}
func tapAlertButton(shouldDismissButton: Bool) {
let showAlertButton = app.buttons["ShowAlertButton"]
showAlertButton.tap()
if shouldDismissButton {
let alert = app.alerts.firstMatch
let alertOKButton = alert.scrollViews.otherElements.buttons["OK"]
alertOKButton.tap()
}
}
func tapNaivgationLink(shouldDismissDestination: Bool) {
let navigationLinkButton = app.buttons["NavigationLinkToDestination"]
navigationLinkButton.tap()
if shouldDismissDestination {
let backButton = app.navigationBars.buttons["WELCOME"]
backButton.tap()
}
}
}
func test_UITestingBootCampView_singUpButton_shouldNotSignIn() {
// Given
signUpAndSignIn(shouldTypeOnKeyboard: false)
// When
let navBar = app.navigationBars["WELCOME"]
// Then
XCTAssertFalse(navBar.exists)
}
func test_UITestingBootCampView_singUpButton_shouldSignIn() {
// Given
signUpAndSignIn(shouldTypeOnKeyboard: true)
// When
let navBar = app.navigationBars["WELCOME"]
// Then
XCTAssertTrue(navBar.exists)
}
네비게이션, 알러트 등 다른 UI 컴포넌트는 UI 테스팅 코드를 통해 조작이 쉬웠는데, 시뮬레이터 HW 연결 시 키보드 입력 이슈가 있어서 시간이 좀 걸렸다. 결과적으로 직접 특정 버튼을 타이핑해서 텍스트 필드에 텍스트를 입력하는게 아니라,
textField.typeText
메소드를 통해 곧바로 텍스트를 넣을 수 있었다.