카테고리 없음

모바일 애플리케이션 테스트 자동화. 파트 2: 전제 조건, 요소 검증 및 단계 독립성

카카오블로거 2021. 10. 27. 02:37

제 이름은 Dmitry Makarenko입니다. 저는 Badoo 및 Bumble의 모바일 QA 엔지니어입니다. 저는 우리 응용 프로그램의 새로운 기능을 수동으로 테스트하고 자동 테스트로 덮고 있습니다.

 

지난 2년 동안 우리 회사의 테스트 자동화 접근 방식이 많이 바뀌었습니다. 테스트 개발에 적극적으로 참여하는 인원이 10명에서 40명으로 늘어났습니다. 그리고 애플리케이션의 모든 새로운 기능은 이제 릴리스 전에 테스트를 거쳐야 합니다. 

 

이러한 환경에서는 가능한 한 빨리 테스트를 개발하는 것이 매우 중요합니다. 그리고 동시에 안정적으로 만들어 지원에 많은 시간이 걸리지 않도록 합니다. 우리는 테스트 개발 속도를 높이고 안정성을 높이는 데 도움이 되는 사례를 공유하기로 결정했습니다. 

 

내 동료인 Viktor Koronevich가 텍스트를 준비하는 데 도움을 주었습니다. 우리는 Heisenbug 회의에서 이 주제에 대해 함께 이야기했습니다 

 

기사의 첫 번째 부분 에서 우리는 프로세스에서 자동화의 역할, 프레임워크의 세부 사항에 대해 이야기했으며 자동 테스트를 만들 때 사용하는 세 가지 방법에 대해 자세히 논의했습니다. 두 번째 부분에서는 요소 상태의 변경 확인, 테스트 전제 조건 설정, 간단하고 복잡한 작업에 대한 단계 개발, 선택적 요소 확인(반드시 수행해야 함)을 다룰 것입니다. 

두 부분의 예는 프로젝트에서 테스트 자동화를 구현하기 시작하는 사람들과 이미 적극적으로 참여하고 있는 사람들에게 동등하게 관련이 있음을 상기시켜 드리겠습니다.

스포일러

실습 4. 요소 상태 변화 확인

아마도 이 관행은 원칙적으로 모바일 자동화에서 가장 중요한 것 중 하나일 것입니다. 왜냐하면 애플리케이션에는 화면에 즉시 표시되고 항상 사용할 수 있는 정적 요소가 거의 없기 때문입니다. 종종 우리는 요소를 로드하는 데 시간이 걸린다는 사실에 직면합니다. 

예를 들어 인터넷 연결이 느린 경우 서버에서 수신한 항목이 상당히 지연되어 표시됩니다. 그리고 그것들이 나타나기 전에 테스트하려고 하면 테스트는 실패할 것입니다. 

따라서 요소 확인을 시작하기 전에 요소가 화면에 나타날 때까지 기다려야 합니다. 당연히 이 문제는 새로운 것이 아니며 표준 솔루션이 있습니다. 예를 들어, Selenium에서는 서로 다른 유형의 대기 메서드인 반면 Calabash에서는 wait_for 메서드입니다. 

우리 자신의 자전거가 필요한 이유 또는 표준 솔루션을 포기한 이유

자동화 프레임워크 구축을 시작할 때 표준 wait_for 메서드를 사용하여 항목이 나타나거나 상태가 변경될 때까지 대기했습니다. 그러나 어느 시점에서 우리는 문제에 봉착했습니다. 때로는 2~3주에 한 번씩 모든 테스트가 중단되었습니다. 어떻게, 왜 이런 일이 일어나고 우리가 무엇을 잘못하고 있는지 이해할 수 없었습니다.

로그를 추가하고 분석한 결과 이러한 중단은 Calabash 프레임워크의 일부인 wait_for 메서드 구현과 관련이 있는 것으로 나타났습니다. wait_for는 전역 스레드에서 구현되는 Ruby Timeout 모듈의 timeout 메서드를 사용합니다. 그리고 이 시간 초과 방법이 우리와 Calabash 프레임워크와 같은 다른 방법에 중첩되어 사용되었을 때 테스트가 중단되었습니다. 

예를 들어 프로필 페이지를 사용자 잠금 버튼으로 스크롤하는 것을 고려하십시오. 

def scroll_to_block_button wait_for(timeout: 30) do ui.scroll_down ui.wait_until_no_animation ui.element_displayed?(BLOCK_BUTTON) end end

wait_for 메소드가 사용되고 있음을 알 수 있습니다. 화면이 아래로 스크롤된 다음 애니메이션이 끝날 때까지 기다렸다가 잠금 버튼의 표시를 확인합니다. 

wait_until_no_animation 메서드의 구현을 살펴보겠습니다.

def wait_until_no_animation wait_for(timeout: 10) do !ui.any_element_animating? end end

wait_until_no_animation 메소드도 wait_for로 구현됩니다. 그는 애니메이션이 화면에서 끝날 때까지 기다립니다. wait_for 내부에서 호출된 wait_for가 다른 메소드를 호출하는 것으로 나타났습니다. Calabash 메소드 내부에서 wait_for에 대한 호출도 있다고 상상해보십시오. wait_for 내부의 wait_for 내부에서 wait_for 체인이 커지면 매달릴 위험이 높아집니다. 따라서 우리는 이 방법의 사용을 포기하고 자체 솔루션을 찾기로 결정했습니다.

이에 대한 요구 사항은 표준 솔루션에 대한 요구 사항과 일치했습니다. 주어진 조건이 충족되거나 할당된 시간이 만료될 때까지 확인을 반복하는 방법이 필요했습니다. 할당된 시간 내에 검사가 실패하면 메서드에서 오류가 발생해야 합니다.

먼저 표준 wait_for 메소드를 반복하는 for 메소드로 Poll 모듈을 생성했습니다. 시간이 지남에 따라 자체 구현을 통해 필요할 때 모듈의 기능을 확장할 수 있었습니다. 지정된 조건에 대해 특정 값을 기대하는 메서드를 추가했습니다. 예를 들어, Poll.for_true 및 Poll.for_false는 실행 코드가 true 또는 false를 반환할 것으로 명시적으로 예상합니다. 아래 예에서는 Poll 모듈과 다른 방법을 사용하는 방법을 보여 드리겠습니다.

다양한 메서드 매개변수도 추가했습니다. return_on_timeout 매개변수를 자세히 살펴보겠습니다. 핵심은 이 매개변수를 사용할 때 지정된 조건이 충족되지 않더라도 Poll.for 메서드가 오류 발생을 중지하고 단순히 검사 결과를 반환한다는 것입니다. 

"어떻게 작동합니까?"라는 질문이 예상됩니다. "이것이 왜 필요한가?" 첫 번째 것부터 시작하겠습니다. Poll.for 메소드에서 2가 3 이상이 될 때까지 기다리면 항상 시간 초과 오류가 발생합니다. 

Poll.for { 2 > 3 } > WaitError

그러나 return_on_timeout 매개변수를 추가하고 2가 3보다 커질 때까지 기다리면 시간 초과가 끝난 후에도 2는 여전히 3보다 크지 않지만 테스트는 실패하지 않고 Poll.for 메서드는 다음을 반환합니다. 이 확인 결과. 

Poll.for(return_on_timeout: true) { 2 > 3 } > false

이것이 필요한 이유는 무엇입니까? return_on_timeout 매개변수를 사용하여 항목의 상태가 변경되었는지 확인합니다. 그러나 실제 테스트 실패를 숨길 수 있으므로 신중하게 수행하는 것이 매우 중요합니다. 잘못 사용하면 오류가 발생했어야 하는 위치에서 지정된 조건이 충족되지 않을 때 테스트가 계속 실행됩니다. 

요소의 상태를 변경하기 위한 옵션

이제 재미있는 부분으로 넘어가겠습니다. 다양한 상태 변경을 확인하는 방법과 일반적으로 어떤 상태 변경이 존재하는지에 대해 이야기해 보겠습니다. 테스트 대상인 검은색 사각형을 만나보세요. 

그는 화면에 나타나는 것과 화면에서 사라지는 것, 두 가지만 알고 있습니다. 

첫 번째 상태 변경 옵션은 "표시되어야 함"입니다. 상태 1 - 테스트 개체가 화면에 없고 상태 2 - 나타나야 하는 경우에 발생합니다. 

나타나야 함

표시되면 확인이 성공한 것입니다. 

상태를 변경하는 두 번째 옵션은 "사라져야 함"입니다. 테스트 개체가 상태 1에 표시될 때 발생하지만 상태 2에 있어서는 안 됩니다. 

없어져야 한다

세 번째 옵션은 처음 두 옵션만큼 명확하지 않습니다. 사실 이 옵션에서 상태의 불변성을 확인하기 때문입니다. "나타나면 안 된다"라고 합니다. 이것은 상태 1에서 테스트 개체가 화면에 표시되지 않고 상태 2에서 일정 시간이 지난 후에도 여전히 나타나지 않아야 할 때 발생합니다. 

나타나지 않아야 함

네 번째 옵션이 무엇인지 짐작했을 것입니다. "잃어선 안 된다"라고 합니다. 이것은 상태 1의 개체가 화면에 표시되고 상태 2의 얼마 후 개체가 여전히 있을 때 발생합니다.

잃어버려서는 안된다

다양한 옵션에 대한 검사 구현

요소의 상태를 변경할 수 있는 모든 옵션을 수정했습니다. 어떻게 확인할 수 있습니까? 구현을 세분화하여 처음 두 옵션을 확인하고 세 번째와 네 번째 옵션을 확인하겠습니다. 

처음 두 옵션의 경우 모든 것이 매우 간단합니다. 전자를 테스트하려면 Poll 메서드를 사용하여 요소가 나타날 때까지 기다려야 합니다. 

# вариант "Должен появиться" Poll.for_true { ui.elements_displayed?(locator) }

두 번째 것을 확인하려면 요소가 사라질 때까지 기다리십시오. 

# вариант "Должен пропасть" Poll.for_false { ui.elements_displayed?(locator) }

그러나 세 번째와 네 번째 옵션의 경우 상황이 그렇게 간단하지 않습니다.

"표시하지 않아야 함" 옵션을 고려하십시오. 

# вариант "Не должен появиться" ui.wait_for_elements_not_displayed(locator) actual_state = Poll.for(return_on_timeout: true) { ui.elements_displayed?(locator) } Assertions.assert_false(actual_state, "Element #{locator} should not appear")

여기서 먼저 화면에 요소가 없는 상태를 수정합니다. 

다음으로, return_on_timeout 매개변수와 함께 Poll.for를 사용하여 항목이 나타날 때까지 기다립니다. 이 경우 Poll.for 메서드는 오류를 발생시키지 않지만 요소가 나타나지 않으면 false를 반환합니다. Poll.for에서 얻은 값은 actual_state 변수에 저장됩니다.

그런 다음 assert 메서드를 사용하여 요소의 불변 상태를 확인합니다.

"Shouldn't lost" 옵션을 테스트하기 위해 유사한 논리를 사용하여 요소가 표시되는 대신 화면에서 사라질 것으로 예상합니다. 

# вариант "Не должен пропасть" ui.wait_for_elements_displayed(locator) actual_state = Poll.for(return_on_timeout: true) { !ui.elements_displayed?(locator) } Assertions.assert_false(actual_state, "Element #{locator} should not disappear")

이 네 가지 상태 변경에 대한 검사는 모바일 애플리케이션의 많은 요소와 관련이 있습니다. 그리고 테스트를 개발하는 사람들이 많기 때문에 누군가가 새 테스트를 만들 때 몇 가지 옵션을 잊어버릴 가능성이 항상 있습니다. 따라서 모든 상태 변경 옵션에 대한 검사 구현을 하나의 메서드로 옮겼습니다.

def verify_dynamic_state(state:, timeout: 10, error_message:) options = { return_on_timeout: true, timeout: timeout, } case state when 'should appear' actual_state = Poll.for(options) { yield } Assertions.assert_true(actual_state, error_message) when 'should disappear' actual_state = Poll.for(options) { !yield } Assertions.assert_true(actual_state, error_message) when 'should not appear' actual_state = Poll.for(options) { yield } Assertions.assert_false(actual_state, error_message) when 'should not disappear' actual_state = Poll.for(options) { !yield } Assertions.assert_false(actual_state, error_message) else raise("Undefined state: #{state}") end end

yield는 이 메서드에 전달된 블록의 코드입니다. 위의 예에서는 elements_displayed?메소드였습니다. 그러나 그것은 우리가 필요로 하는 요소의 상태를 반영하는 다른 방법일 수 있습니다. 루비 문서 .

따라서 하나의 메서드를 호출하여 모든 요소의 상태를 변경하는 옵션을 확인할 수 있으므로 모든 테스트 팀의 수명이 크게 간소화됩니다.

결론: 

  • UI 요소를 확인할 때 상태를 변경하는 네 가지 옵션을 모두 잊지 않는 것이 중요합니다.
  • 이러한 검사를 일반적인 방법으로 가져오는 것이 유용합니다. 

모든 상태 변경에 대해 완전한 검사 시스템을 사용하는 것이 좋습니다. 우리는 무엇을 의미합니까? 요소가 존재하면 참이고 존재하지 않으면 거짓이라고 상상해 보십시오. 


조건 1 조건 2
나타나야 함 거짓 진실
없어져야 한다 진실 거짓
나타나지 않아야 함 거짓 거짓
잃어버려서는 안된다 진실 진실

우리는 모든 조합의 매트릭스를 구축하고 있습니다. 새로운 상태가 나타나면 테이블을 확장하고 새로운 조합을 얻을 수 있습니다. 

연습 5. 테스트 전제 조건을 안정적으로 구성하기

제목에서 짐작할 수 있듯이 이 섹션의 목적은 테스트를 시작하기 전에 전제 조건을 설정하는 방법을 파악하는 것입니다.

두 가지 예를 살펴보겠습니다. 첫 번째는 설정에서 iOS의 위치 서비스를 비활성화하는 것입니다. 두 번째는 채팅 기록을 만드는 것입니다. 

첫 번째 예에서 iOS에서 위치 서비스를 비활성화하는 방법의 구현은 다음과 같습니다. 

def switch_off_location_service ui.wait_for_elements_displayed(SWITCH) if ui.element_value(SWITCH) == ON ui.tap_element(SWITCH) ui.tap_element(TURN_OFF) end end

스위치(스위치 요소)가 화면에 나타날 때까지 기다리고 있습니다. 그런 다음 상태를 확인합니다. 예상한 것과 일치하지 않으면 변경합니다. 

그런 다음 설정을 닫고 응용 프로그램을 시작합니다. 때로는 갑자기 문제가 발생합니다. 어떤 이유로 위치 서비스가 활성화된 상태로 유지됩니다. 어떻게 작동합니까? 우리는 그것을 끄기 위해 모든 것을했습니다. iOS 시스템의 시스템 설정에 문제가 있는 것 같습니다. 설정을 빠르게 종료하면(스위치를 누른 직후 테스트가 수행됨) 새 상태가 저장되지 않습니다. 그러나 응용 프로그램에서 전제 조건을 설정할 때 문제가 발생할 수 있습니다.

테스트를 시작하기 전에 채팅 기록을 만드는 두 번째 예를 들어 보겠습니다. 메서드 구현은 다음과 같습니다. 

def send_message(from:, to:, message:, count:) count.times do QaApi.chat_send_message(user_id: from, contact_user_id: to, message: message) end end

QAAPI  사용하여 user_id로 메시지를 보냅니다. 루프에서 필요한 수의 메시지를 보냅니다. 

그런 다음 채팅으로 이동하여 메시지 기록 다운로드와 관련된 필요한 확인을 수행합니다. 그러나 때때로 모든 메시지가 채팅에 표시되지는 않습니다. 때때로 우리는 채팅이 열리기 전에가 아니라 이미 채팅에 있는 동안 계속 수신합니다. 이는 서버가 필요한 수의 메시지를 즉시 보낼 수 없기 때문입니다. 경우에 따라 배송이 지연됩니다. 

두 예의 공통적인 문제를 강조해 보겠습니다. 전제 조건이 잘못 설정되면 응용 프로그램 자체가 예상대로 작동하더라도 후속 검사에서 테스트가 실패합니다. 우리는 그 이유를 이해하기 시작하고, 그 다음에는 사전 조건의 부적절한 준비로 인해 추락이 발생한다는 것을 이해합니다. 이러한 상황은 테스터뿐만 아니라 새로운 기능을 개발할 때 테스트 실행을 분석하는 개발자에게도 좌절감을 줍니다.

이 문제를 어떻게 해결할 수 있습니까? 우리는 사전 조건화 방법에 행동 보장을 추가할 수 있습니다. 

그러면 위치 서비스를 비활성화하는 방법은 다음과 같습니다.

def ensure_location_services_switch_in_state_off ui.wait_for_elements_displayed(SWITCH) if ui.element_value(SWITCH) == ON ui.tap_element(SWITCH) ui.tap_element(TURN_OFF) Poll.for(timeout_message: 'Location Services should be disabled') do ui.element_value(SWITCH) == OFF end end end

Poll.for 메서드를 사용하여 테스트의 다음 단계로 이동하기 전에 토글 상태가 변경되었는지 확인합니다. 이렇게 하면 위치 서비스가 때때로 활성화되어 발생하는 문제를 피할 수 있습니다. 

두 번째 예에서는 QAAPI 방법이 다시 도움이 될 것입니다. 

def send_message(from:, to:, message:, count:) actual_messages_count = QaApi.received_messages_count(to, from) expected_messages_count = actual_messages_count + count count.times do QaApi.chat_send_message(user_id: from, contact_user_id: to, message: message) end QaApi.wait_for_user_received_messages(from, to, expected_messages_count) end

메시지를 보내기 전에 채팅에서 현재 수의 메시지를 수신한 후 필요한 수의 메시지가 전송되었는지 확인합니다. 이 검사 후에만 테스트가 계속 실행됩니다. 따라서 응용 프로그램에서 채팅을 열 때 필요한 모든 메시지를 보고 필요한 검사를 수행할 수 있습니다.

따라서 테스트에서 전제 조건을 설정할 때 항상 조치 보장을 사용하는 것이 좋습니다. 테스트는 준비 단계에서 즉시 충돌하므로 사전 조건이 실패하는 경우 충돌을 조사하는 시간을 절약할 수 있습니다. 

일반적인 지침으로 비동기 작업이 수행될 때 사전 설정이 설정되었는지 확인하는 검사를 테스트에 추가하는 것이 좋습니다. 

Martin Fowler의 기사 에서 이 섹션  설명된 문제에 대해 자세히 읽을 수 있습니다 

실습 6. 간단하고 복잡한 작업 또는 테스트 단계의 독립성

간단한 작업

이전 섹션의 결론에 따라 모든 작업이 완료되었는지 테스트에 확인을 추가해야 하는 것처럼 보일 수 있습니다. 그러나 실제로는 그렇지 않습니다. 이 섹션에서는 다양한 작업 및 확인을 위한 단계를 구현하는 방법에 대해 설명합니다. 이것은 테스트의 거의 모든 단계에서 작동하는 매우 일반적인 공식입니다. 따라서 평소와 같이 구체적인 예를 고려할 것입니다.

GIF 메시지를 찾고 보내는 테스트부터 시작하겠습니다. 

먼저 메시지를 보내려는 사용자와 채팅을 시작해야 합니다. 

When primary_user opens Chat with chat_user

그런 다음 GIF 메시지의 입력 필드를 엽니다. 

And primary_user switches to GIF input source

다음으로 GIF 이미지에 대한 검색어를 입력하고 목록이 업데이트되었는지 확인하고 목록에서 마음에 드는 이미지를 제출하고 제출되었는지 확인해야 합니다.

And primary_user searches for "bee" GIFs And primary_user sends 7th GIF in the list Then primary_user verifies that the selected GIF has been sent

전체 스크립트는 다음과 같습니다.

Scenario: Searching and sending GIF in Chat Given users with following parameters | role | name | | primary_user | Dima | | chat_user | Lera | And primary_user logs in When primary_user opens Chat with chat_user And primary_user switches to GIF input source And primary_user searches for "bee" GIFs And primary_user sends 7th GIF in the list Then primary_user verifies that the selected GIF has been sent

gif를 찾는 단계에 주목합시다. 

And(/^primary_user searches for "(.+)" GIFs$/) do |keyword| chat_page = Pages::ChatPage.new.await TestData.gif_list = chat_page.gif_list chat_page.search_for_gifs(keyword) Poll.for_true(timeout_message: 'Gif list is not updated') do (TestData.gif_list & chat_page.gif_list).empty? end end

여기에서 거의 모든 다른 단계와 마찬가지로 다음을 수행합니다.

  1. 먼저 원하는 페이지(ChatPage)가 열리기를 기다립니다. 
  2. 그런 다음 사용 가능한 모든 GIF 목록을 저장합니다. 
  3. 그런 다음 검색 키워드를 입력하십시오. 
  4. 그런 다음 상태 변경을 기다립니다. 목록이 업데이트됩니다(결국 테스트에 작업 확인을 추가하는 것이 유용하다는 사실에 대해 이야기했습니다).

모든 것이 올바르게 구현된 것 같습니다. 검색을 완료한 후 이미지 목록이 업데이트되었는지 확인하고 그 후에야 이미지 중 하나를 보냅니다. 그러나 예를 들어 동일한 검색어를 입력한 후 이미지 목록이 업데이트되지 않는지 확인하는 테스트를 작성하려는 경우 문제가 발생합니다. 이 경우 GIF 이미지에 대한 검색어를 입력하기 위한 별도의 단계를 만들어야 하며, 이는 기존 이미지를 크게 복제합니다.

action이 다른 결과로 이어질 수 있는 경우에도 유사한 문제가 발생하며, 하나의 단계에서 action의 완료를 확인하는 형태로 그 중 하나만 기록합니다. 이것은 이러한 종류의 단계를 재사용하는 복잡성으로 이어지며 따라서 테스트 개발 및 코드 복제가 느려집니다.

이것을 어떻게 피할 수 있습니까? 눈치채셨겠지만 GIF ​​검색 단계에는 실제로 세 단계가 포함되었습니다. 

  1. 현재 목록 저장;
  2. 찾다;
  3. 목록 업데이트를 확인하십시오.

재사용 문제에 대한 해결책은 이 단계를 세 개의 단순하고 독립적인 단계로 나누는 것입니다. 

첫 번째 단계는 현재 이미지 목록을 저장하는 것입니다.

And(/^primary_user stores the current list of GIFs$/) do TestData.gif_list = Pages::ChatPage.new.await.gif_list end

두 번째 단계 - gif 찾기 - 검색 키워드를 입력할 수 있습니다. 

And(/^primary_user searches for "(.+)" GIFs$/) do |keyword| Pages::ChatPage.new.await.search_for_gifs(keyword) end

세 번째 단계에서는 목록이 업데이트되기를 기다리고 있습니다. 

And(/^primary_user verifies that list of GIFs is updated$/) do chat_page = Pages::ChatPage.new.await Poll.for_true(timeout_message: 'Gif list is not updated') do (TestData.gif_list & chat_page.gif_list).empty? end end

결과적으로 초기 스크립트는 다음과 같습니다.

Scenario: Searching and sending GIF in Chat Given users with following parameters | role | name | | primary_user | Dima | | chat_user | Lera | And primary_user logs in When primary_user opens Chat with chat_user And primary_user switches to GIF input source And primary_user stores the current list of GIFs And primary_user searches for "bee" GIFs Then primary_user verifies that list of GIFs is updated When primary_user sends 7th GIF in the list Then primary_user verifies that the selected GIF has been sent

따라서 테스트에서 목록이 업데이트되지 않은 경우에도 처음 두 단계를 사용할 수 있습니다. 이를 통해 다양한 테스트에서 간단한 단계를 재사용할 수 있으므로 개발 시간을 절약할 수 있습니다. 

그러나 여기에도 뉘앙스가 있습니다. 단계가 항상 단순하고 독립적일 수는 없습니다. 그러한 경우 우리는 그것들을 복합물이라고 부를 것입니다. 

복잡한 작업

복잡한 단계는 애플리케이션의 서로 다른 화면 간 전환을 포함하거나 화면 상태를 변경하는 작업을 의미합니다. 미니 게임에서 투표하는 예를 들어 이러한 상황을 생각해 봅시다.

미니 게임은 그에게 글을 쓴 다른 사람들의 사용자 프로필을 제공하는 화면입니다. 메시지에 답장하거나 이 사용자를 건너뛸 수 있습니다. 통과 조치를 "아니오 투표"라고 합시다. 

테스트 사용자

N 번 "투표"하지 않고 게임 화면을 닫았다가 다시 열고 사용자가 올바른 위치에 있는지 확인하는 테스트를 작성해야 합니다.

"반대"는 간단한 행동입니다. 그러나 그를 위해 간단한 단계를 수행하면 N 번 투표하려면 스크립트 수준에서 이 단계를 동일한 횟수만큼 사용해야 합니다. 그런 대본을 읽는 것은 불편합니다. 따라서 필요한 만큼 투표할 수 있는 "투표 수" 매개변수를 사용하여 보다 복잡한 단계를 만드는 것이 좋습니다. 

그러한 단계를 구현하는 것은 매우 쉬운 일인 것 같습니다. 페이지에서 필요한 횟수만큼 투표하면 됩니다. 

When(/^primary_user votes No in Messenger mini game (\d+) times$/) do |count| page = Pages::MessengerMiniGamePage.new.await count.to_i.times do page.vote_no end end

그러나 여기서 우리는 문제에 직면해 있습니다. 때때로 테스트가 너무 빨리 투표하는 경우가 있습니다. 즉, 이전 프레스가 처리되기 전에 버튼을 누릅니다. 이 경우 응용 프로그램은 서버에 새 음성을 보내지 않습니다. 우리의 발걸음은 성공할 것입니다. 그리고 다음 단계에서 사용자가 게임에서 올바른 위치에 있는지 확인하려고 할 때 이전 단계가 작업을 완료하지 않았고 필요한 것보다 적은 투표를 했기 때문에 테스트가 실패합니다.

테스트 전제 조건을 설정하는 경우와 마찬가지로 여기에서도 이전 단계가 제대로 작동하지 않았기 때문에 테스트와 애플리케이션이 올바르게 작동하는 단계에서 충돌을 처리해야 합니다. 물론 그런 상황을 좋아하는 사람은 아무도 없으므로 문제를 해결해야 합니다.

When(/^primary_user votes No in Messenger mini game (\d+) times$/) do |count| page = Pages::MessengerMiniGamePage.new.await count.to_i.times do progress_before = page.progress page.vote_no Poll.for_true do page.progress > progress_before end end end

각 투표를 완료한 후 미니 게임에서 진행 상황 변경에 대한 확인을 추가합니다. 그래야만 다음 투표를 시도할 수 있습니다. 이렇게 하면 모든 투표가 처리되는 데 시간이 걸리고 테스트 충돌을 피할 수 있습니다.

요약해보자. 우리는 간단한 행동을 위한 독립적인 단계를 만듭니다. 이렇게 하면 유사한 단계를 다시 작성해야 하는 경우보다 짧은 시간에 쉽게 재사용하고 다른 스크립트를 생성할 수 있습니다. 복잡한 작업의 단계에서 다음 단계로 이동하기 전에 작업이 완료되었는지 확인하는 검사를 추가합니다. 

이러한 권장 사항을 요약하기 위해 테스트에서 간단한 작업에 대한 독립적인 방법을 분리하고 복잡한 작업에 사전 설정 검사를 추가하는 것이 좋습니다. 

실습 7. 선택적 요소 검증

선택적 요소란 일부 조건에 따라 동일한 화면에 표시되거나 표시되지 않을 수 있는 요소를 의미합니다. 여기에서는 사용자 작업 또는 경고(경고)를 확인하기 위한 대화 상자의 예를 살펴보겠습니다.

대화 상자의 예

다양한 모바일 애플리케이션에서 비슷한 대화를 접했을 것입니다. 예를 들어 우리의 두 응용 프로그램에는 70개 이상이 있으며 다른 사용자 작업에 대한 응답으로 다른 위치에 나타납니다. 그들에 대한 선택적 요소는 무엇입니까? 

위의 스크린샷을 분석해 보겠습니다.

  • 스크린샷 1: 제목, 설명 및 두 개의 버튼.
  • 스크린샷 2: 제목, 설명 및 버튼 1개.
  • 스크린샷 3: 설명 및 두 개의 버튼.

따라서 이러한 대화 상자의 선택적 요소는 제목과 두 번째 버튼입니다. 경고의 요소에 대한 로케이터는 애플리케이션의 위치 또는 표시되는 사용자 작업 이후에 관계없이 동일합니다. 따라서 기본 페이지에서 모든 유형의 대화 상자에 대한 검사를 한 번 구현한 다음 이 코드를 반복할 필요가 없도록 각 특정 경고를 상속하려고 합니다. 우리는 이 섹션에서 그러한 검사의 생성을 고려할 것입니다. 

각 대화 상자를 확인하기 위한 메서드 호출이 어떻게 보이는지 시작해 보겠습니다.

class ClearAccountAlert < AppAlertAndroid def verify_alert_lexemes verify_alert(title: ClearAccount::TITLE, description: ClearAccount::MESSAGE, first_button: ClearAccount::OK_BUTTON, last_button: ClearAccount::CANCEL_BUTTON) end endclass WaitForReplyAlert < AppAlertAndroid def verify_alert_lexemes verify_alert(title: WaitForReply::TITLE, description: WaitForReply::MESSAGE, first_button: WaitForReply::CLOSE_BUTTON) end endclass SpecialOffersAlert < AppAlertAndroid def verify_alert_lexemes verify_alert(description: SpecialOffers::MESSAGE, first_button: SpecialOffers::SURE_BUTTON, last_button: SpecialOffers::NO_THANKS_BUTTON) end end

모든 예에서 우리는 verify_alert 메소드를 호출하고 토큰을 전달하여 필수 항목을 확인합니다. 동시에 WaitForReplyAlert는 존재하지 않아야 하기 때문에 두 번째 버튼에 대한 토큰을 전달하지 않으며 SpecialOffersAlert는 제목에 대한 토큰입니다.

verify_alert 메소드의 구현을 고려하십시오.

def verify_alert(title: nil, description:, first_button:, last_button: nil) ui.wait_for_elements_displayed([MESSAGE, FIRST_ALERT_BUTTON]) ui.wait_for_element_text(expected_lexeme: title, locator: ALERT_TITLE) if title ui.wait_for_element_text(expected_lexeme: description, locator: MESSAGE) ui.wait_for_element_text(expected_lexeme: first_button, locator: FIRST_ALERT_BUTTON) ui.wait_for_element_text(expected_lexeme: last_button, locator: LAST_ALERT_BUTTON) if last_button end

먼저 필요한 요소가 화면에 나타날 때까지 기다립니다. 그런 다음 해당 텍스트가 메서드에 전달된 토큰과 일치하는지 확인합니다. 선택적 요소의 경우 토큰이 메서드에 전달되었는지 텍스트를 확인하고 전달되지 않은 경우 아무 작업도 수행하지 않습니다.

이 접근 방식의 문제점은 무엇입니까? 요점은 선택적 요소가 표시되지 않아야 할 때 표시되지 않는지 확인을 건너뛰는 것입니다. 이것은 코믹한 상황으로 이어질 수 있습니다. 예를 들어 다음과 같은 경고가 나타날 수 있습니다.

사용자는 무엇을 선택해야 하는지 이해하지 못합니다. 두 버튼 모두 대화 상자를 닫는 것 같습니다. 치명적인 버그인 것 같습니다. 응용 프로그램이 충돌하지 않더라도 수정해야 합니다. 그리고 이러한 문제를 식별할 수 있도록 테스트를 변경해야 합니다.

이를 위해 테스트에서 수표를 변경합니다. 

ui.wait_for_element_text(expected_lexeme: title, locator: ALERT_TITLE) if title

~에

if title.nil? Assertions.assert_false(ui.elements_displayed?(ALERT_TITLE), "Alert title should not be displayed") else ui.wait_for_element_text(expected_lexeme: title, locator: ALERT_TITLE) end

if 조건을 변경하고 두 번째 조건 검사를 추가했습니다. 선택적 요소에 대한 토큰을 전달하지 않으면 이 요소가 화면에 없어야 하며 이를 확인합니다. 제목에 어떤 텍스트가 있으면 이 텍스트와 함께 요소가 있어야 함을 이해하고 확인합니다. 우리는 이 논리를 wait_for_optional_element_text라는 일반 메서드로 분할하기로 결정했습니다. 이 예제의 대화 상자뿐만 아니라 선택적 요소가 있는 응용 프로그램의 다른 화면에도 이 방법을 사용할 수 있습니다. 위의 예에서 if-조건이 완전히 새 메서드 내부에 있음을 알 수 있습니다.

def wait_for_optional_element_text(expected_lexeme:, locator:) GuardChecks.not_nil(locator, 'Locator should be specified') if expected_lexeme.nil? Assertions.assert_false(elements_displayed?(locator), "Element with locator #{locator} should not be displayed") else wait_for_element_text(expected_lexeme: expected_lexeme, locator: locator) end end

verify_alert 메서드의 구현도 변경되었습니다. 

def verify_alert(title: nil, description:, first_button:, last_button: nil) ui.wait_for_elements_displayed([MESSAGE, FIRST_ALERT_BUTTON]) ui.wait_for_optional_element_text(expected_lexeme: title, locator: ALERT_TITLE) ui.wait_for_element_text(expected_lexeme: description, locator: MESSAGE) ui.wait_for_element_text(expected_lexeme: first_button, locator: FIRST_ALERT_BUTTON) ui.wait_for_optional_element_text(expected_lexeme: last_button, locator: LAST_ALERT_BUTTON) end

우리는 여전히 필요한 요소가 화면에 나타날 때까지 기다리고 있습니다. 그런 다음 해당 텍스트가 메서드에 전달된 토큰과 일치하는지 확인합니다. 선택적 요소의 경우 이제 wait_for_optional_element_text 메서드를 사용하여 선택적 요소가 표시되지 않아야 할 때 표시되지 않도록 합니다.  

요약하면 선택적 검사가 없다는 사실에 주의를 기울이고 싶습니다. 모든 상태를 검사해야 하는 선택적 요소가 있습니다. 그리고 선택적 요소의 상태 확인을 공통 방법으로 분리하면 응용 프로그램의 다른 화면에서 쉽게 재사용할 수 있습니다.

요약: 필요한 요소의 모든 상태를 확인하고 동일한 유형의 작업에 대한 일반적인 방법을 강조하기 위해 완전한 시스템을 사용하는 것이 좋습니다. 

비슷한 권장 사항이 이미 이 기사에서 언급되었음을 알 수 있습니다. 트릭은 사용 예가 다양하다는 것입니다. 

일반 권장 사항 

지금까지 설명한 7가지 사례에서 모바일 애플리케이션 테스트 자동화를 위한 주요 권장 사항을 강조해 보겠습니다.

  • 검증은 우리가 테스트를 작성하는 것이기 때문에 항상 완전한 검증 시스템을 사용하십시오. 
  • 비동기 작업에 대한 사전 설정 검사를 추가하는 것을 잊지 마십시오. 
  • 동일한 유형의 코드를 재사용하기 위한 일반적인 방법을 강조 표시합니다(단계 및 페이지의 방법 모두).
  • 테스트 대상을 단순하게 유지하십시오. 
  • 테스트에서 간단한 작업을 위해 독립적인 방법을 분리합니다. 

아마도 이 팁은 누군가에게는 분명해 보일 것입니다. 그러나 우리는 그것들이 다른 상황에 적용될 수 있고 적용되어야 한다는 사실에 주의를 기울이고자 합니다. 다른 유용한 권장 사항으로 목록을 보완하려면 의견을 환영합니다! 

보너스

기사에 설명된 모든 사례를 반영하는 테스트 프로젝트를 준비했습니다. 링크를 따라 공부하고 적용하십시오: