E2E Automate testingใน iOS ด้วย Cucumber กัน

Chayawat Suntornrak
odds.team
Published in
4 min readMay 23, 2024

เริ่มด้วยมาทำความรู้จัก Cucumber ก่อน มันคืออะไรหละ ทำไมต้องใช้?

  • Testing tool ที่ช่วยทำ automate test ในรูปแบบ BDD(Behavior-Driven Development) ที่ทำการสร้าง Test Case ขึ้นมาก่อนที่จะมา Coding กัน
  • เข้าใจง่ายด้วยภาษา Gherkin รูปแบบเหมือน Human language ที่ทุกคนอ่านเข้าใจทั้งในเชิง Business และ Technical ไม่ว่าจะเป็น PO, BA, QA, Dev รวมไปถึงใครก็ตามก็สามารถอ่านและเข้าใจได้
  • Implement กับภาษาอื่นๆได้มากมาย เช่น javascript, java, kotlin เป็นต้นและอื่นๆ
  • Keyword เบื้องต้นที่ควรรู้

Given => พูดง่ายๆ คือ state เริ่มต้น เช่น ตอนนี้อยู่หน้า login

When => เป็น action ของ user ว่าทำอะไร เช่น กดปุ่มอะไรซักอย่างหนึ่งปุ่ม

Then => ผลลัพท์ที่จะเกิดขึ้นจาก When เช่น แสดง Alert ขึ้นมาหลังจากกดปุ่ม

เริ่มด้วยเราสร้าง Application iOS กัน (ไม่พ้นหน้า login จริงๆ)

ซึ่งมีโจทย์ให้ว่าต้อง login ด้วย

  • email: “oak@odds.team”
  • password: “1234”

ถ้าถูกต้องจะมีแถบ status ขึ้นมาว่า “login successed”

ภาพแสดงการ login ผ่าน

ถ้าเกิดผิดจะมีแถบ status ขึ้นมาว่า “Incorrect username or password”

ภาพแสดงการ login ไม่ผ่าน

สร้างโปรเจคไว้เริ่มทดสอบกัน

  • ไปที่เราอยากสร้างโปรเจคแล้ว “npm init”
** เพิ่มสิ่งที่จำเป็นที่จะต้องใช้ใน package.json
{
"@wdio/appium-service": "^8.36.1",
"@wdio/cli": "^8.36.1",
"@wdio/cucumber-framework": "^8.36.1",
"@wdio/local-runner": "^8.36.1",
"@wdio/spec-reporter": "^8.36.1",
"appium": "^2.5.4",
"appium-uiautomator2-driver": "^3.1.0",
"appium-xcuitest-driver": "^7.15.2",
"ts-node": "^10.9.2"
}
  • npm install
  • npx wdio config (WebdriverIO)

เป็น configuration helper เพื่อ generate config file ขึ้นมา อ่านเพิ่มเติมได้ที่ https://webdriver.io/docs/testrunner

หลังจากที่เรา generate config file เสร็จแล้วเราจะเห็น Structure project ที่หน้าตาประมาณนี้

มันคืออะไรกันมั่งหละ

  • wdio.conf.js

ส่วนหลักๆ อยากให้มาดูแถวๆ capabilities จะเป็นการกำหนดว่าเราจะ เทสอะไรที่ไหน ยังไง

capabilities: [{
// capabilities for local Appium web tests on iOS
"platformName": "iOS",
"appium:deviceName": "iPhone SE (3rd generation)",
"appium:automationName": "XCUITest",
"appium:platformVersion": "17.2",
"appium:app": "/.../Developer/Xcode/DerivedData/CucumberNaJa-eafptjwpeyiolsdwawugtwtutbah/Build/Products/Debug-iphonesimulator/CucumberNaJa.app",
}]

platformName: iOS/Android

automationName: ใช้ XCUITest เป็น framework ของ iOS สำหรับ UI testing

app: directory ที่เก็บตัว app (ในที่นี้อยู่ที่ DerivedData)

  • file .feature ส่วนนี้คือส่วนของ cucumber ถ้าให้เข้าใจง่ายๆก็คือส่วนของ business logic (จะเห็นว่าใครๆก็อ่านแล้วเข้าใจได้)
@login <== ส่วนนี้คือ tag(กำหนดตอน run test ได้ว่าจะให้ run tag ไหน)
Feature: Login

Scenario Outline: As a user, I input collect username and password
Given I am on the login screen
When I login with email "oak@odds.team" and password "1234"
And I press login button
Then I should see status login successed

Scenario Outline: As a user, I input wrong username or password
Given I am on the login screen
When I login with email "oak@odds.team" and password "12345"
And I press login button
Then I should see status login incorrect
  • file .js ที่เห็นอยู่ใน step-definitions คือส่วนของ technical ที่เป็นภาษา javascript ศึกษาคำสั่งต่างๆได้ที่ (https://webdriver.io/docs/api/element)
const { expect } = require('@wdio/globals')
const { Given, When, Then } = require("@wdio/cucumber-framework");


Given('I am on the login screen', async () => {
const loginLabel = await $('~loginLabel'); ==> หา id ที่ชื่อ loginLabel

expect(loginLabel).toBeDefined();
});

When('I login with email {string} and password {string}', async (email, password) => {
const emailTextField = await $('~emailTextField');
const passwordTextField = await $('~passwordTextField');

await emailTextField.click(); ==> Click ที่ช่อง email
await emailTextField.setValue(email); ==> set value ให้กับช่อง email
await passwordTextField.click();
await passwordTextField.setValue(password);
});

When('I press login button', async () => {
const loginButton = await $('~loginButton');
await loginButton.click();
})

Then('I should see status login successed', async () => {
const statusLabel = await $('~statusLabel');
const value = await statusLabel.getValue();

expect(value).toEqual('login successed');
});

Then('I should see status login incorrect', async () => {
const statusLabel = await $('~statusLabel');
const value = await statusLabel.getValue();

expect(value).toEqual('Incorrect username or password');
});
  • package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wdio": "wdio run ./wdio.conf.js"
}

มาสังเกตุแถวๆ scripts จะเห็นว่ามี “wdio” เป็นการ run ทุก .feature file เรา โดย ใช้คำสั่งว่า “npm run wdio

เพิ่มเติม ถ้าเรามีหลายๆ Feature และอยากระบุว่าให้ run Feature ไหนได้โดยการเพิ่ม “wdio:tag” (ชื่อที่ตั้งขึ้นมาเอง)

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wdio": "wdio run ./wdio.conf.js",
"wdio:tag": "wdio run ./wdio.conf.js --cucumberOpts.tags=\"$npm_config_tag\""
}

โดยใช้คำสั่งว่า

* (ในที่นี้เรามี feature login ตามหัวข้อ file .feature)
npm run wdio:tag --tag="@login"

Appium inspector (https://appium.github.io/appium-inspector/latest/quickstart/installation/)

เครื่องมือสำหรับ inspect mobile

{
"platformName": "iOS",
"appium:deviceName": "iPhone SE (3rd generation)",
"appium:automationName": "XCUITest",
"appium:platformVersion": "17.2",
"appium:app": "/.../Developer/Xcode/DerivedData/CucumberNaJa-eafptjwpeyiolsdwawugtwtutbah/Build/Products/Debug-iphonesimulator/CucumberNaJa.app",
}

ทีน้ีเราก็จะเห็น accessibility id กันแล้ว

บทความนี้ก็ถือว่าเป็นการเขียนคร่าวๆให้ได้เห็นภาพรวมๆกัน

หวังว่าจะเป็นประโยชน์กับทุกคนไม่มากก็น้อยครับ

--

--