เทสแอพด้วย Appium ง่ายๆ ไม่ต้องจิ้มเอง

เขียน automate test ทีเดียว ได้ทั้ง Android และ iOS

Travis P
Black Lens
Published in
4 min readAug 25, 2017

--

ผมเชื่อว่าเป็นเรื่องปกติของ developer ทุกคนนะครับ ที่พอเราเขียนโค้ดเสร็จแล้วก็ต้องมานั่งเทสว่าทำงานถูกต้องมั้ย ดั้งเดิมเลยผมเขียนโค้ดเสร็จส่วนหนึ่ง ก็ต้องมารันลง Nexus 5 คู่ใจนั่งกดดูว่าโค้ดเราทำงานถูกต้องมั้ย ซึ่งก็เสียเวลาไม่ใช่น้อย ถ้าผิดก็ต้องแก้แล้วรันใหม่อีกทีจนกว่าจะถูก

พักหลังผมทำ unit test เป็น ชีวิตก็ดีขึ้น เขียนโค้ดเสร็จฟังก์ชั่นนึง ผมก็สามารถเทสเฉพาะฟังก์ชั่นนั้นได้โดยไม่ต้องรันใส่มือถืออีกต่อไป แต่มันก็เลี่ยงไม่ได้แหละ ถ้าผมเขียนโค้ดเสร็จครบหน้านึง ก็ต้องรันลงมือถือมาจิ้มเองอยู่ดี

และด้วยความหลากหลายของ Android ทำให้ผมต้องลองจิ้มหลายเครื่องจนกว่าจะมั่นใจ สร้างความเบื่อหน่ายเป็นอย่างมาก!! โอว ไม่ 😱 พอกันที 😡

Appium

Appium เป็น framework ช่วยในการเทสโมบายแอพ ทั้งเนทีฟ ไฮบริด เว็บแอพ และอื่นๆ Appium ต่อยอดมาจาก Selenium ซึ่งมีพื้นฐานอยู่บน WebDriver อีกที

การทำงานของ Appium จะแบ่งเป็น 2 ส่วนคือ

  1. Appium server เชื่อมต่อกับ emulator หรือเครื่องจริงๆ รอฟังคำสั่ง
  2. Appium client คุยกับ server ผ่าน REST API เพื่อสั่ง device ทำงานตามต้องการ

ด้วยความที่มันคุยกันผ่าน REST API นี่แหละทำให้เรามีอิสระในการเลือกใช้ภาษาในการเขียนเทส ไม่ว่าจะเป็น Ruby, Python, Java, JavaScript, PHP, C# หรือ RobotFramework ซึ่งก็มีคนทำ library ภาษาต่างๆไว้แล้วแหละ เราแค่หยิบมาใช้

Appium Java Client + JUnit

แน่นอนว่าแอนดรอยเดฟอย่างผมก็ต้องเลือก Java อยู่แล้ว การใช้งานก็ง่ายมากๆ ง่ายไม่ต่างกับการทำ unit test เลย… ก็เพราะขั้นตอนมันเหมือนการทำ unit test นั่นแหละ แทนที่เราจะ assert หรือ verify ตามปกติ ก็เขียนสั่งหา element สั่งกดปุ่มแทน จากนั้นก็รันเทสด้วย JUnit Runner แล้ว Appium client ก็ไปสั่ง server ให้ device ทำตามสั่ง ถ้าหา element ไม่เจอก็เทสไม่ผ่าน ขึ้นตัวแดงมาบอก ถ้าเทสผ่านจนจบก็ได้สีเขียวมาสบายใจ

Setup

เมื่อก่อนการ install Appium เป็นเรื่องยุ่งยาก ต้องมานั่ง brew install npm install เองหลายอย่าง แถมบางที version ไม่ตรง document ก็ไปต่อไม่ถูก ต้องไปหาวิธีแก้เอาเอง แต่เดี๋ยวนี้ง่ายขึ้นเยอะ เตรียมตัวตามนี้เลย

Appium Desktop

เปิดขึ้นมาแล้วกด Start Server พร้อมใช้งานทันที

Android Sample App

แอพตัวอย่างเราประกอบด้วย 1 TextView และ 1 Button กดปุ่มจะเปลี่ยน TextView จากคำว่า Label เป็น Hello Appium จบ

iOS Sample App

ฝั่ง iOS ก็ล้อกันมาเลย ประกอบด้วย 1 Label และ 1 Button กดปุ่มจะเปลี่ยน Label จากคำว่า Label เป็น Hello Appium จบ

Test Project

เปิด Java/Kotlin IDE โปรดของผม IntelliJ ขึ้นมา กดสร้าง project

หรือจะไม่สร้างเองก็ clone ตัวอย่างมาดูตามไปได้เลย

ตรงนี้คุณมีทางเลือก 3 ทาง

  1. Java ธรรมดา แต่เวลาจะใช้ library ใดๆ ต้องไปโหลด .jar มาใส่เอง
  2. Maven ที่มี dependency management คล้ายๆกับ gradle ผมก็จะไม่คุ้นเคยหน่อยๆ แต่ถ้าจะเอาไปรันบน AWS Device Farm ก็ต้องอันนี้แหละ
  3. Gradle ที่เราชาว Android คุ้นเคย

สำหรับโพสนี้ผมเลือก Maven ละกัน เพราะผม setup gradle project ลองรันแล้วมันพังหาสาเหตุไม่ได้ ฮาๆ

หลังจากเติม kotlin เรียบร้อยก็มาดูส่วน dependencies ของ pom.xml กัน ตรงนี้ก็อารมณ์ dependencies ใน build.gradle แหละ

<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jre8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>5.0.0-BETA9</version>
</dependency>
</dependencies>

เติม dependency ของ appium java client เข้าไป แล้ว intellij มันจะถามว่าให้ download library มาเลยมั้ย เราก็ตอบให้มัน auto import เข้ามา

TestCase

หน้าตาอย่างกะ JUnit ธรรมด๊าธรรมดา กวาดตามองคร่าวๆก็จะพบว่า

val platform = MobilePlatform.ANDROID เอาไว้เปลี่ยน Android/iOS

สร้าง AppiumDriver ใน @Before fun setup() {} โดยส่ง DesiredCapabilities ตามต้องการเข้าไปเพื่อบอก Appium ว่ารันบนเครื่องลักษณะไหน อย่าลืมใส่ที่อยู่ของ .apk/.app ให้เรียบร้อย

driver.manage().timeouts().implicitlyWait(30L, TimeUnit.SECOND) ไว้บอกให้รอเวลาเราอยากหา element อะไรสักอย่าง แต่มันยังไม่โผล่ขึ้นมาบนจอ ถ้าไม่ใส่ไว้อาจจะหาไม่เจอ ทั้งๆที่มันแค่ยังไม่โผล่ขึ้นมาก็ได้

@Test fun testGreet() {} ไว้เขียนเทส จะตั้งชื่อยังไงก็แล้วแต่ ขอแค่มี @Test ผมใช้ pattern page object อธิบายสั้นๆคือ แทนที่เราจะเขียนโค้ดหา element ใน test เราย้ายโค้ดพวกนั้นไปไว้ใน page แทน แยกส่วนระหว่างขั้นตอนการเทสกับ implementation details ไว้อย่างชัดเจน

อันที่จริงวิธีผมก็ไม่ชัดเจนเท่าไร เพราะ pattern page object ทั่วไปเค้ามักสั่ง page.clickMyButton() เลย เค้าจะไม่ทำ page.myButton.click() คือเค้าจะยิ่งซ่อน implementation details เข้าไปใหญ่ซึ่งเป็นเรื่องดี แต่ผมขี้เกียจ เขียนเยอะ ถ้าสนใจไปดูต่อกันได้

Main Page Object

มาดู MainPage กันบ้าง ก็แค่มี property ตาม element บนหน้าจอ เราต้องแปะ lateinit เอาไว้ เพื่อให้ PageFactory.initElements() ทีหลังใน init block

PageFactory จะทำการหา element ตาม platform ที่รันเทสอยู่ มาใส่ property ที่เรา annotate @AndroidFindBy @iOSFindBy ไว้
Android ก็จะหาตาม id (นำหน้าด้วย package name) ส่วน iOS สามารถหาได้ 2 แบบคือ หาจาก accessibility id หรือ xpath แต่ผมหาแบบ accessibility id แล้วพัง จึงต้องหาแบบ xpath

หา id และ xpath จาก Appium Desktop

แน่นอนว่าพวกเราชาว developer รู้ id อยู่แล้ว เอามาใส่ตาม pattern ของมันได้ แต่ถ้าเราเป็น tester เฉยๆล่ะ! ก็ถาม dev ได้อยู่ดี แต่ appium ก็มีของเล่นให้

กด Start New Session จากนั้นกรอก capability คล้ายๆตอนเรา setup test
เปิด emulator ขึ้นมา (หรือเสียบเครื่องจริง)
กด Start Session รอจน inspector โผล่ขึ้นมา
เอาเมาส์จิ้ม element แต่ละตัว ดู id ได้จากทางขวา

iOS ก็เช่นกัน ใส่ capability ให้ครบแล้วดู accessibility id หรือ xpath ทางขวา

บางที xpath ก็จะมึนๆ เช่นในนี้ //XCUIElementTypeStaticText[@name="Label"] แต่ผมต้องใส่ //XCUIElementTypeStaticText[1] ถึงจะไม่พัง อันนี้ก็ต้องมั่วๆกันไป

Run the test

จะเทสบน Android/iOS ก็เปลี่ยนตัวแปร platform เอา การรันเทสก็กดปุ่ม play หน้า test function เหมือน unit test ปกติ

appium ก็จะเปิดแอพขึ้นมา หา myLabel มาเช็ค myLabel.text == “Label”
จากนั้นกดปุ่ม myButton แล้วเช็ค myLabel.text == “Hello Appium” อีกที

เทสผ่าน สีเขียว สบายตา

สรุป

Appium ช่วยให้เราเทส 2 platform ได้ด้วย code เดียว ยิ่งเราใช้ page object ก็ยิ่ง clean

แต่ทุกอย่างไม่ได้ราบรื่นเสมอไป การเซต desired capabilities มีความจุกจิก ต้องใส่ให้ถูกต้อง บางทีใส่ไม่ถูก error ก็สื่อความหมายไม่ครบ กว่าจะแก้ได้ต้องงมอยู่นาน

appium client แต่ละภาษาก็มีความแตกต่างกันพอสมควร feature แต่ละอันก็ครบบ้างไม่ครบบ้าง บางที client version นึงก็ไม่สนับสนุน server version นึง

document มีความกระจัดกระจาย บางอันไม่ครบ บางอันไม่อัพเดต

ด้วยปัจจัยที่กล่าวมาข้างต้น สำหรับมือใหม่จะรู้สึกสับสนก็ไม่แปลก ผมเองก็สับสน อาจจะต้องงมทางกันหน่อย แต่ถ้าเรา setup เรียบร้อยแล้ว หรือมีทีมเฉพาะทางมาทำตรงนี้ก็โอเคครับ

--

--

Travis P
Black Lens

Android Developer, Kotlin & Flutter Enthusiast and Gamer