이 영역을 누르면 첫 페이지로 이동
스니펫 텔러 | Snippet Teller 블로그의 첫 페이지로 이동

스니펫 텔러 | Snippet Teller

페이지 맨 위로 올라가기

Swift로 🍏 UI XCTest 기반 잘 다지는 법 #2 - Action

스니펫 텔러 | Snippet Teller

Swift로 🍏 UI XCTest 기반 잘 다지는 법 #2 - Action

  • 2020.04.04 22:15
  • 👨🏻‍💻 QA이야기/📱 모바일자동화
반응형

💥 Extension 잘 사용하기

Swift의 extension을 잘 사용하면, 테스트 코드가 매우 깔끔해지고, 관리하기도 쉬워지며 새로운 테스트를 작성하기도 수월해진다.

 

기본적인 코드는

XCUIApplication().tabBars.buttons["Home"].tap()

이러한 방식이다. 매우 간단하지만, XCUIApplication().app()을 매번 불러야하며, Page Object 컨셉을 적용하면, 더욱 코드가 더러워진다.

 

XCTest 라이브러리의 Action Extension을 만들어서 관리해주면, 매우 간편하게 코드를 만들수 있다:

extension XCTest {
struct UIApplication {
static var app: XCUIApplication?
}
var app: XCUIApplication {
get {
if UIApplication.app == nil { UIApplication.app = XCUIApplication() }
return UIApplication.app!
}
}
func tap(_ element: XCUIElement) {
element.tap()
}
}

 

이후 테스트 코드는 다음처럼 훨씬 더 읽기 쉬워지며, 깔끔해진다:

tap(app.tabBars.buttons["Home"])

 

다음 코드들을 내가 작성한 많이 쓰이고 유용한 Action function들이다:

// Element가 화면에 안보이면 스크롤하여 찾아내는 function이다.
func locateToElement(element: XCUIElement) {
if elementIsOnWindow(element) == false {
scrollUntilElementAppears(element)
}
}
private func elementIsOnWindow(_ element: XCUIElement) -> Bool {
guard element.exists && element.frame.isEmpty == false && element.isHittable else { return false }
return app.windows.element(boundBy: 0).frame.contains(element.frame)
}
private func scrollUntilElementAppears(_ element: XCUIElement, threshold: Int = 5) {
var iteration = 0
while elementIsWithinWindow(element) == false {
guard iteration < threshold else { break }
scrollDown()
iteration += 1
}
while elementIsWithinWindow(element) == false {
guard iteration > 0 else { break }
scrollUp()
iteration -= 1
}
}
func isKeyboardHidden() -> Bool {
let keyboard = app.keyboards.element(boundBy: 0)
return keyboard.children(matching: .any).count == 0
}
func scrollDown(times: Int = 1) {
var startYPoint = 0.70
var endYPoint = 0.40
if isKeyboardHidden() == false {
startYPoint = 0.50
endYPoint = 0.20
}
let topScreenPoint = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: startYPoint))
let bottomScreenPoint = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: endYPoint))
for _ in 0..<times {
topScreenPoint.press(forDuration: 0, thenDragTo: bottomScreenPoint)
}
}
func scrollUp(times: Int = 1) {
var startYPoint = 0.40
var endYPoint = 0.70
if isKeyboardHidden() == false {
startYPoint = 0.20
endYPoint = 0.50
}
let topScreenPoint = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: endYPoint))
let bottomScreenPoint = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: startYPoint))
for _ in 0..<times {
bottomScreenPoint.press(forDuration: 0, thenDragTo: topScreenPoint)
}
}
// Element가 보이거나 보이지 않을때까지 기다리는 function들이다. 리턴값도 필요하면 가져다 사용할 수 있다.
let waitElementTimeoutInterval: TimeInterval = 5
let waitElementTimeoutLongInterval: TimeInterval = 30
@discardableResult func waitForElementToAppear(element: XCUIElement, forced: Bool = false, message: String? = nil, longWait: Bool = false) -> Bool {
let expectation = XCTNSPredicateExpectation(predicate: NSPredicate.elementExists,
object: element)
let result = XCTWaiter().wait(for: [expectation], timeout: longWait ? waitElementTimeoutLongInterval: waitElementTimeoutInterval)
if forced {
if result != .completed {
XCTFail(message ?? "Test failed due to element not being displayed")
}
return true
} else {
return result == .completed
}
}
@discardableResult func waitForElementToDisappear(element: XCUIElement, forced: Bool = false, message: String? = nil, longWait: Bool = false) -> Bool {
let expectation = XCTNSPredicateExpectation(predicate: NSPredicate.elementNotExists,
object: element)
let result = XCTWaiter().wait(for: [expectation], timeout: longWait ? waitElementTimeoutLongInterval: waitElementTimeoutInterval)
if forced {
if result != .completed {
XCTFail(message ?? "Test failed due to element displayed unexpectedly")
}
return true
} else {
return result == .completed
}
}
extension NSPredicate {
static let elementExists: NSPredicate = NSPredicate(format: "exists == true")
static let elementNotExists: NSPredicate = NSPredicate(format: "exists == false")
}
// Input 필드에 벨류를 입력하는 function이다.
func enterValue(textField: XCUIElement, value: String, takeAction: Bool) {
tap(textField)
if textField.value != nil && (textField.value as! String) != value {
if (textField.value as! String).isEmpty == false && (textField.value as! String) != (textField.placeholderValue)! {
clearText(textField)
}
textField.typeText(value)
if takeAction { textField.typeText("\n") }
}
}
private func clearText(_ textField: XCUIElement) {
let cleatTextButton = textField.buttons["Clear text"]
if cleatTextButton.exists {
tap(cleatTextButton)
} else {
var length = (textField.value as! String).count
while (length > 0) {
textField.doubleTap()
textField.typeText(XCUIKeyboardKey.delete.rawValue)
if (textField.value as! String).count == length { break } else {
length = (textField.value as! String).count
}
}
}
}
// Label에 정해진 밸류를 포함하고 있는 버튼을 찾아 탭 하는 function이다.
func tapButtonWithLabelContains(_ partialLabel: String) {
let buttonPredicate = NSPredicate(format: "label CONTAINS '\(partialLabel)'")
let button = app.buttons.element(matching: buttonPredicate)
tap(button)
}

 

반응형
이 글은 (새창열림) 본 저작자 표시, 비영리, 변경 금지 규칙 하에 배포할 수 있습니다. 자세한 내용은 Creative Commons 라이선스를 확인하세요.
본 저작자 표시
비영리
변경 금지

'👨🏻‍💻 QA이야기 > 📱 모바일자동화' 카테고리의 다른 글

Kotlin으로 ☕ Espresso UI 테스트 기반 잘 다지는 법 #2 - Matchers  (0) 2020.04.19
Kotlin으로 ☕ Espresso UI 테스트 기반 잘 다지는 법 #1 - 테스트 케이스  (0) 2020.04.12
Swift로 🍏 UI XCTest 기반 잘 다지는 법 #1 - 테스트 케이스  (0) 2020.04.04
🥒 Cucumber 이해하고 잘 쓰는 방법  (0) 2020.04.03
🗳️ QA의 모바일 자동화를 위한 개발환경  (0) 2020.04.01

댓글

댓글을 사용할 수 없습니다.

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • Kotlin으로 ☕ Espresso UI 테스트 기반 잘 다지는 법 #2 - Matchers

    Kotlin으로 ☕ Espresso UI 테스트 기반 잘 다지는 법 #2 - Matchers

    2020.04.19
  • Kotlin으로 ☕ Espresso UI 테스트 기반 잘 다지는 법 #1 - 테스트 케이스

    Kotlin으로 ☕ Espresso UI 테스트 기반 잘 다지는 법 #1 - 테스트 케이스

    2020.04.12
    ⚙️ 기본 설정 Espresso (에스프레소) 는 구글에서 제공하는 안드로이드 UI 테스트 라이브러리이다. 3.x 버전부터는 AndroidX Library에 통합되어 제공되고 있다. 기본적인 설정은 안드로이드 개발 코드 안에서 직접 환경설정할 수 있다. build.gradle android { defaultConfig { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } } // https://developer.android.com/jetpack/androidx/releases/test def androidxTest = '1.2.0' def espresso = '3.2.0' dependencies { …. androidTestI…
  • Swift로 🍏 UI XCTest 기반 잘 다지는 법 #1 - 테스트 케이스

    Swift로 🍏 UI XCTest 기반 잘 다지는 법 #1 - 테스트 케이스

    2020.04.04
  • 🥒 Cucumber 이해하고 잘 쓰는 방법

    🥒 Cucumber 이해하고 잘 쓰는 방법

    2020.04.03
다른 글 더 둘러보기

정보

스니펫 텔러 | Snippet Teller 블로그의 첫 페이지로 이동

스니펫 텔러 | Snippet Teller

  • 스니펫 텔러 | Snippet Teller의 첫 페이지로 이동

검색

카테고리

  • 전체보기 (467)
    • 📋 일상이야기 (20)
    • 🇩🇪 베를린이야기 (42)
    • 🇰🇷 한국이야기 (3)
    • 🏅 리뷰이야기 (79)
      • 해외직구 (6)
      • 온라인서비스 (20)
      • 유럽생활 (15)
      • 그 외 (38)
    • 👨🏻‍💻 QA이야기 (19)
      • ⚙️ Quality Engineering (12)
      • 📱 모바일자동화 (7)
    • 🎗 생각하는 글 (49)
    • 🎮 게임정보 (253)
반응형

공지사항

  • 공지 - 블로그 공지사항

방문자

  • 전체 방문자
  • 오늘
  • 어제

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기

정보

무워 | hsoochun의 스니펫 텔러 | Snippet Teller

스니펫 텔러 | Snippet Teller

무워 | hsoochun

블로그 구독하기

  • 구독하기
  • RSS 피드
Powered by Tistory / Kakao. © 무워 | hsoochun. Designed by Fraccino.

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.