Apa itu end-to-end testing ?
Sebagai programer, tentu kita semua udah kenal yang namanya testing. Ada macam-macam test yang bisa kita pake untuk meningkatkan kualitas kode dan/atau aplikasi yang kita buat. Yang paling umum dan wajib adalah unit-test untuk memvalidasi alur/logika program dalam unit yang paling kecil yaitu function.
End-to-end testing, sering disingkat E2E, adalah untuk memvalidasi cara kerja aplikasi atau website dari sudut pandang seorang user. Jadi dalam E2E testing kita tulis skrip untuk mensimulasikan behavior atau tindakan-tindakan seorang user — mirip bikin bot.
Dalam artikel ini, saya akan bahas tentang E2E testing pake sebuah framework yang bernama The Intern. Ada beberapa framework yang bisa kita pake selain The Intern, antara lain: Cypress, Nightwatch, TestCafe, dan yang paling tua, Selenium. Berdasarkan pengalaman saya, The Intern adalah yang paling seimbang antara fitur & tingkat kerumitannya.
Instalasi
Sebelum mulai, pastikan dulu Anda punya NodeJS di komputer. Kalo belum silakan instal dulu.
Saya pake Mac/Linux. Jadi kalo Anda pake Windows, silakan sesuaikan sendiri perintahnya
Yuk kita mulai dengan pembuatan direktori kerja, sebut saja myproject
. Dan karena The Intern adalah aplikasi NodeJS, langsung sekalian kita inisialisasi NPM & instal modulnya.
$ mkdir myproject && cd myproject
$ npm init -y
$ npm i -D intern
Berikutnya, kita bikin direktori untuk skrip testing yang nanti kita buat & sekalian bikin file konfigurasi untuk The Intern.
$ mkdir testing
$ touch intern.json
Buka file intern.json
& isi dengan konfigurasi seperti di bawah:
{
"functionalSuites": ["tests/**/*.js"],
"functionalTimeouts": {
"pageLoad": 10000,
"connectTimeout": 10000,
"find": 5000
},
"environments": [
{
"browserName": "chrome",
"fixSessionCapabilities": false
}
]
}
functionalSuites
menentukan di mana lokasi skrip testing yang harus dijalankan. Di bawahnya functionalTimeouts
mengatur berapa lama waktu yang dipunyai The Intern sebelum men-trigger error. Jadi dalam setingan di atas, kalo page nggak muncul setelah 10 detik (10000ms), baru dianggap error. Kalo nyari elemen di halaman, 5 detik nggak ketemu baru error.
Setingan environments
menentukan browser apa saja yang ingin kita pake untuk ngetes. Di sini kita pake Chrome aja.
Coba jalanin di terminal pake npx
,
$ npx intern
TOTAL: tested 0 platforms, 0 passed, 0 failed
The Intern ini sebenernya juga bisa dipake untuk unit-test, tapi di sini kita fokus untuk functional-test aja. Lagian unit-test lebih enak kalo pake Jest.
Aplikasi Kalkulator
Kita coba ngetes aplikasi kalkulator yang ada di sini.
Bikin file tests/calculator.js
, isinya begini:
const { suite, test, before } = intern.getPlugin('interface.tdd');
const { assert } = intern.getPlugin('chai');
suite('Kalkulator', ()=>{
// function ini dijalanin satu kali sebelum test
before(async ({remote})=>{
// buka halaman yang mau dites
await remote.get('https://projects.masputih.com/calculator/');
})
})
Test Suite adalah kumpulan beberapa tes yang konteksnya sama atau saling berhubungan. Di sini kita bikin Suite dengan nama Kalkulator
. Function before
, dijalanin satu kali sebelum The Intern menjalankan tes di dalam sebuah Suite.
Dalam kode di atas, sebelum jalanin tes kita buka dulu halaman yang mau dites pake remote.get()
. Jadi remote
itu apa? remote
adalah objek yang dikirim The Intern ke semua tes, berisi API yang nantinya kita pake untuk mencari elemen di halaman yang kita tes dan lain-lain.
Dokumentasi tentang API apa aja yang bisa kita pake bisa dibaca di sini.
Sekarang coba jalanin The Intern. Chrome akan terbuka sebentar terus nutup sendiri.
$ npx intern
Listening on localhost:9000 (ws 9001)
Tunnel started
‣ Created remote session chrome 74.0.3729.157 on Mac OS X (cc252706f18da600d371b8e3bace5d88)
TOTAL: tested 1 platforms, 0 passed, 0 failed
Sekilas pas Chrome belum nutup, kita bisa liat status di bawah address bar seperti di bawah:
Sekarang coba kita cari id
elemen yang jadi layar kalkulator. Pake Chrome, kita bisa liat id
elemennya adalah answer
.
Jadi kita update kode testingnya untuk ngetes apakah elemen layarnya ada dan tampilan awalnya adalah 0
. Karena nanti kita bakal ngecek tampilan layar di dalam tes-tes yang lain, kita simpan referensi ke elemen #answer
dalam variabel display
.
const { suite, test, before } = intern.getPlugin('interface.tdd');
const { assert } = intern.getPlugin('chai');
suite('Kalkulator', ()=>{
let display;
before(async ({remote})=>{
await remote.get('https://projects.masputih.com/calculator/');
})
test('Kalkulator ada di page, tombol lengkap', async ({remote})=>{
display = await remote.findById('answer');
const displayText = await display.getVisibleText();
assert.equal(displayText,'0');
})
})
Jalanin The Intern,
$ npx intern
Listening on localhost:9000 (ws 9001)
Tunnel started
‣ Created remote session chrome 74.0.3729.157 on Mac OS X (71f3f793bd62afa5ef01b199966a09bf)
✓ chrome 74.0.3729.157 on Mac OS X - Kalkulator - Kalkulator ada di page, tombol lengkap (0.042s)
TOTAL: tested 1 platforms, 1 passed, 0 failed
Semua API Intern bersifat asynchronous, jadi semua tes kita bisa ditulis pake
async-await
. Apa itu? Bisa dibaca sendiri di Javascript:Async-Await.
Berikutnya, kita pastiin semua tombol ada. Untuk tombol numerik kita simpen dalam array numericButtons
. Tombol yang lain punya variabel sendiri-sendiri. Tujuannya biar kita bisa pake tombol-tombol itu dalam tes yang lain tanpa harus manggil API remote.findXXX
lagi. Pertama, kita liat dulu atribut apa yang bisa kita pake untuk nyari button.
Dari Chrome dev tool kita liat, hampir semua button punya atribut value
, sisanya ada yang pake id
. Jadi di sini kita pake findDisplayedByCssSelector
& findDisplayedById
. Dan karena kita hanya mau ngetes tombol yang bersangkutan ada atau nggak, kita ngeceknya cukup pake assert.exists()
.
Dalam tutorial ini, saya pake API yang namanya
assert
yang lazim dipake dalam TDD (Test-Driven Development). Chai, library yang dipake oleh Intern juga mendukung jenis API yang lain yaituexpect
danshould
yang dipake dalam BDD (Behavior-Driven Development). Dokumentasi API-nya bisa dibaca sendiri di Chai:Assert dan Chai:BDD.Mau pake API yang model TDD atau BDD itu hanya masalah selera aja. Oke?
Lanjut, kita update lagi tesnya untuk ngecek semua tombol.
const { suite, test, before } = intern.getPlugin('interface.tdd');
const { assert } = intern.getPlugin('chai');
suite('Kalkulator', ()=>{
let display,
ACBtn, CEBtn, addBtn, subtractBtn, multiplyBtn, divideBtn, eqBtn, decimalBtn,
numericButtons = [];
before(async ({remote})=>{
await remote.get('https://projects.masputih.com/calculator/');
})
test('Kalkulator ada di page, tombol lengkap', async ({remote})=>{
display = await remote.findById('answer');
const displayText = await display.getVisibleText();
assert.equal(displayText,'0');
const btn0 = await remote.findDisplayedById('zeroButton');
assert.exists(btn0);
numericButtons.push(btn0);
const btn1 = await remote.findDisplayedByCssSelector('button[value="1"]');
assert.exists(btn1);
numericButtons.push(btn1);
const btn2 = await remote.findDisplayedByCssSelector('button[value="2"]');
assert.exists(btn2);
numericButtons.push(btn2);
const btn3 = await remote.findDisplayedByCssSelector('button[value="3"]');
assert.exists(btn3);
numericButtons.push(btn3);
const btn4 = await remote.findDisplayedByCssSelector('button[value="4"]');
assert.exists(btn4);
numericButtons.push(btn4);
const btn5 = await remote.findDisplayedByCssSelector('button[value="5"]');
assert.exists(btn5);
numericButtons.push(btn5);
const btn6 = await remote.findDisplayedByCssSelector('button[value="6"]');
assert.exists(btn6);
numericButtons.push(btn6);
const btn7 = await remote.findDisplayedByCssSelector('button[value="7"]');
assert.exists(btn7);
numericButtons.push(btn7);
const btn8 = await remote.findDisplayedByCssSelector('button[value="8"]');
assert.exists(btn8);
numericButtons.push(btn8);
const btn9 = await remote.findDisplayedByCssSelector('button[value="9"]');
assert.exists(btn9);
numericButtons.push(btn9);
decimalBtn = await remote.findDisplayedByCssSelector('button[value="."]');
assert.exists(decimalBtn);
ACBtn = await remote.findDisplayedByCssSelector('button[value=ac]');
assert.exists(ACBtn);
CEBtn = await remote.findDisplayedByCssSelector('button[value=ce]');
assert.exists(CEBtn);
divideBtn = await remote.findDisplayedByCssSelector('button[value="/"]');
assert.exists(divideBtn);
multiplyBtn = await remote.findDisplayedByCssSelector('button[value="*"]');
assert.exists(multiplyBtn);
subtractBtn = await remote.findDisplayedByCssSelector('button[value="-"]');
assert.exists(subtractBtn);
addBtn = await remote.findDisplayedByCssSelector('button[value="+"]');
assert.exists(addBtn);
eqBtn = await remote.findDisplayedById('equalButton');
assert.exists(eqBtn);
});
})
Jalanin Intern,
$ npx intern
Listening on localhost:9000 (ws 9001)
Tunnel started
‣ Created remote session chrome 74.0.3729.157 on Mac OS X (062d2f77bf3c360bccba6c786334a128)
✓ chrome 74.0.3729.157 on Mac OS X - Kalkulator - Kalkulator ada di page, tombol lengkap (0.572s)
TOTAL: tested 1 platforms, 1 passed, 0 failed
Berikutnya kita tes apakah tombol angka & layar display berfungsi.
const { suite, test, before } = intern.getPlugin('interface.tdd');
const { assert } = intern.getPlugin('chai');
suite('Kalkulator', ()=>{
let display,
ACBtn, CEBtn, addBtn, subtractBtn, multiplyBtn, divideBtn, eqBtn, decimalBtn,
numericButtons = [];
before(async ({remote})=>{
await remote.get('https://projects.masputih.com/calculator/');
})
test('Kalkulator ada di page, tombol lengkap', async ({remote})=>{
// ... dst
});
test('Tombol angka', async ({remote})=>{
let displayText;
// klik tombol angka 1
await numericButtons[1].click();
// cek display, harus berisi angka 1
displayText = await display.getVisibleText();
assert.equal(displayText, '1');
// klik tombol angka 2
await numericButtons[2].click();
// cek display, harus berisi angka 12
displayText = await display.getVisibleText();
assert.equal(displayText, '12');
// klik tombol angka 3
await numericButtons[3].click();
// cek display, harus berisi angka 123
displayText = await display.getVisibleText();
assert.equal(displayText, '123');
})
})
$ npx intern
Listening on localhost:9000 (ws 9001)
Tunnel started
‣ Created remote session chrome 74.0.3729.157 on Mac OS X (ba742b32775ccbae55377bc8eb73ec6d)
✓ chrome 74.0.3729.157 on Mac OS X - Kalkulator - Kalkulator ada di page, tombol lengkap (0.555s)
✓ chrome 74.0.3729.157 on Mac OS X - Kalkulator - Tombol angka (0.147s)
TOTAL: tested 1 platforms, 2 passed, 0 failed
Lanjut, tes tombol AC
& tombol-tombol operator,
const { suite, test, before } = intern.getPlugin('interface.tdd');
const { assert } = intern.getPlugin('chai');
suite('Kalkulator', ()=>{
let display,
ACBtn, CEBtn, addBtn, subtractBtn, multiplyBtn, divideBtn, eqBtn, decimalBtn,
numericButtons = [];
before(async ({remote})=>{
await remote.get('https://projects.masputih.com/calculator/');
})
test('Kalkulator ada di page, tombol lengkap', async ({remote})=>{
// ... dst
});
test('Tombol angka', async({remote})=>{
// ... dst
})
test('Tombol AC', async ({remote})=>{
await ACBtn.click();
let displayTextAfter = await display.getVisibleText();
assert.equal(displayTextAfter,'0');
})
test('Tombol operator', async ({remote})=>{
// +
await numericButtons[3].click();
await addBtn.click();
await numericButtons[1].click();
await eqBtn.click();
let displayText = await display.getVisibleText();
assert.equal(displayText, '4');
// -
await subtractBtn.click();
await numericButtons[1].click();
await eqBtn.click();
displayText = await display.getVisibleText();
assert.equal(displayText, '3');
// x
await multiplyBtn.click();
await numericButtons[3].click();
await eqBtn.click();
displayText = await display.getVisibleText();
assert.equal(displayText, '9');
// :
await divideBtn.click();
await numericButtons[2].click();
await eqBtn.click();
displayText = await display.getVisibleText();
assert.equal(displayText, '4.5');
})
})
$ npx intern
Listening on localhost:9000 (ws 9001)
Tunnel started
‣ Created remote session chrome 74.0.3729.157 on Mac OS X (e907e52d0d9f89e9e41315c5c0110782)
✓ chrome 74.0.3729.157 on Mac OS X - Kalkulator - Kalkulator ada di page, tombol lengkap (0.552s)
✓ chrome 74.0.3729.157 on Mac OS X - Kalkulator - Tombol angka (0.153s)
✓ chrome 74.0.3729.157 on Mac OS X - Kalkulator - Tombol AC (0.046s)
✓ chrome 74.0.3729.157 on Mac OS X - Kalkulator - Tombol operator (0.41s)
TOTAL: tested 1 platforms, 4 passed, 0 failed
Jadi begitulah caranya jalanin tes end-to-end atau functional test dengan The Intern. Banyak perintah/function yang bisa kita pake untuk query elemen DOM selain yang kita pake di atas. Daftarnya bisa dibaca sendiri di Leadfoot:Command.
Kalo mau nyoba nerusin testing sendiri bisa dicoba ngetes tombol EC
& jalanin operasi matematika yang panjang.
Semoga bermanfaat.
Also in this category ...
- » Mengenal Hook di ReactJS
- » Rust 101: Hello Rust
- » JS : Pemrograman Asinkron
- » Cara Mudah Pakai Environment Variable dengan Webpack
- » ReactJS : Higher Order Component