() translation by (you can also view the original English article)



Protractor adalah framework pengujian end-to-end yang populer yang memungkinkan Anda menguji aplikasi Angular Anda pada browser nyata yang mensimulasikan interaksi browser seperti cara pengguna nyata berinteraksi dengannya. Pengujian end-to-end dirancang untuk memastikan bahwa aplikasi berfungsi sesuai harapan dari perspektif pengguna. Selain itu, tes tidak memperhatikan implementasi kode aktual.
Protractor berjalan di atas Selenium WebDriver yang populer, yang merupakan API untuk otomatisasi dan pengujian browser. Selain fitur yang disediakan oleh Selenium WebDriver, Protractor menawarkan pencari dan metode untuk menangkap komponen UI aplikasi Angular.
Dalam tutorial ini, Anda akan belajar tentang:
- mengatur, mengkonfigurasi dan menjalankan Protractor
- menulis tes dasar untuk Protractor
- objek halaman dan mengapa Anda harus menggunakannya
- pedoman yang harus dipertimbangkan saat menulis tes
- menulis tes E2E untuk aplikasi dari awal hingga selesai
Bukankah itu terdengar menarik? Namun, hal pertama yang pertama.
Apakah Saya Perlu Menggunakan Protractor?
Jika Anda menggunakan Angular-CLI, Anda mungkin tahu bahwa secara default, ia dikirim dengan dua framework untuk pengujian. Mereka:
- tes unit menggunakan Jasmine dan Karma
- tes end-to-end menggunakan Protractor
Perbedaan yang jelas antara keduanya adalah bahwa yang pertama digunakan untuk menguji logika komponen dan layanan, sedangkan yang kedua digunakan untuk memastikan bahwa fungsionalitas tingkat tinggi (yang melibatkan elemen UI) dari aplikasi berfungsi seperti yang diharapkan.
Jika Anda baru menguji di Angular, saya akan merekomendasikan membaca Komponen Pengujian dalam seri Angular Using Jasmine untuk mendapatkan ide yang lebih baik tentang di mana menggambar garis.
Dalam kasus ini, Anda dapat memanfaatkan kekuatan utilitas pengujian Angular dan Jasmine untuk menulis tidak hanya tes unit untuk komponen dan layanan, tetapi juga tes UI dasar. Namun, jika Anda perlu menguji fungsionalitas front-end aplikasi Anda dari awal sampai akhir, Protractor adalah cara untuk pergi. Protractor API dikombinasikan dengan pola desain seperti objek halaman membuatnya lebih mudah untuk menulis tes yang lebih mudah dibaca. Berikut ini contoh untuk membuat semuanya berjalan lancar.
1 |
/*
|
2 |
1. It should have a create Paste button
|
3 |
2. Clicking the button should bring up a modal window
|
4 |
*/
|
5 |
|
6 |
it('should have a Create Paste button and modal window', () => { |
7 |
|
8 |
expect(addPastePage.isCreateButtonPresent()).toBeTruthy("The button should exist"); |
9 |
expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy("The modal window shouldn't exist, not yet!"); |
10 |
|
11 |
addPastePage.clickCreateButton(); |
12 |
|
13 |
expect(addPastePage.isCreatePasteModalPresent()).toBeTruthy("The modal window should appear now"); |
14 |
});
|
Mengkonfigurasi Protractor
Menyiapkan Protractor mudah jika Anda menggunakan Angular-CLI untuk menghasilkan proyek Anda. Struktur direktori yang dibuat oleh ng new
adalah sebagai berikut.
1 |
.
|
2 |
├── e2e |
3 |
│ ├── app.e2e-spec.ts |
4 |
│ ├── app.po.ts |
5 |
│ └── tsconfig.e2e.json |
6 |
├── karma.conf.js |
7 |
├── package.json |
8 |
├── package-lock.json |
9 |
├── protractor.conf.js |
10 |
├── README.md |
11 |
├── src |
12 |
│ ├── app |
13 |
│ ├── assets |
14 |
│ ├── environments |
15 |
│ ├── favicon.ico |
16 |
│ ├── index.html |
17 |
│ ├── main.ts |
18 |
│ ├── polyfills.ts |
19 |
│ ├── styles.css |
20 |
│ ├── test.ts |
21 |
│ ├── tsconfig.app.json |
22 |
│ ├── tsconfig.spec.json |
23 |
│ └── typings.d.ts |
24 |
├── tsconfig.json |
25 |
└── tslint.json |
26 |
|
27 |
5 directories, 19 files |
Template proyek default yang dibuat oleh Protractor tergantung pada dua file untuk menjalankan tes: file spesifikasi yang berada di dalam direktori e2e dan file konfigurasi (protractor.conf.js). Mari kita lihat bagaimana protractor.conf.js dapat dikonfigurasi adalah:
1 |
/* Path: protractor.conf.ts*/
|
2 |
|
3 |
// Protractor configuration file, see link for more information
|
4 |
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
5 |
|
6 |
const { SpecReporter } = require('jasmine-spec-reporter'); |
7 |
|
8 |
exports.config = { |
9 |
allScriptsTimeout: 11000, |
10 |
specs: [ |
11 |
'./e2e/**/*.e2e-spec.ts' |
12 |
],
|
13 |
capabilities: { |
14 |
'browserName': 'chrome' |
15 |
},
|
16 |
directConnect: true, |
17 |
baseUrl: 'https://localhost:4200/', |
18 |
framework: 'jasmine', |
19 |
jasmineNodeOpts: { |
20 |
showColors: true, |
21 |
defaultTimeoutInterval: 30000, |
22 |
print: function() {} |
23 |
},
|
24 |
onPrepare() { |
25 |
require('ts-node').register({ |
26 |
project: 'e2e/tsconfig.e2e.json' |
27 |
});
|
28 |
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); |
29 |
}
|
30 |
};
|
Jika Anda baik-baik saja dengan menjalankan pengujian di browser web Chrome, Anda dapat membiarkannya seperti ini dan melewati sisa bagian ini.
Mengatur Protractor Dengan Selenium Standalone Server
directConnect: true
memungkinkan Protractor terhubung langsung ke driver browser. Namun, pada saat menulis tutorial ini, Chrome adalah satu-satunya browser yang didukung. Jika Anda memerlukan dukungan multi-browser atau menjalankan browser selain Chrome, Anda harus menyiapkan server mandiri Selenium. Langkah-langkahnya adalah sebagai berikut.
Instal Protractor secara global menggunakan npm:
1 |
npm install -g protractor |
Ini menginstal alat baris perintah untuk webdriver-manager bersama dengan protractor. Sekarang perbarui webdriver-manager untuk menggunakan binari terbaru, dan kemudian jalankan server mandiri Selenium.
1 |
webdriver-manager update |
2 |
|
3 |
webdriver-manager start |
Akhirnya, atur directConnect: false
dan tambahkan properti seleniumAddress
sebagai berikut:
1 |
capabilities: { |
2 |
'browserName': 'firefox' |
3 |
},
|
4 |
directConnect: false, |
5 |
baseUrl: 'http://localhost:4200/', |
6 |
seleniumAddress: 'http://localhost:4444/wd/hub', |
7 |
framework: 'jasmine', |
8 |
jasmineNodeOpts: { |
9 |
showColors: true, |
10 |
defaultTimeoutInterval: 30000, |
11 |
print: function() {} |
12 |
},
|
File konfigurasi di GitHub menyediakan informasi lebih lanjut tentang opsi konfigurasi yang tersedia di Protractor. Saya akan menggunakan opsi default untuk tutorial ini.
Menjalankan Pengujian
ng e2e
adalah satu-satunya perintah yang Anda perlukan untuk mulai menjalankan tes jika Anda menggunakan Angular-CLI. Jika tes tampak lambat, itu karena Angular harus mengkompilasi kode setiap kali Anda menjalankan ng e2e
. Jika Anda ingin mempercepatnya, inilah yang harus Anda lakukan. Sajikan aplikasi menggunakan ng serve
.
Kemudian jalankan tab konsol baru dan jalankan:
1 |
ng e2e -s false |
Tes harus memuat lebih cepat sekarang.
Tujuan Kami
Kami akan menulis tes E2E untuk aplikasi dasar Pastebin. Klon proyek dari repo GitHub.
Baik versi, versi starter (yang tanpa tes) dan versi final (yang dengan tes), tersedia di cabang terpisah. Mengkloning cabang pemula untuk saat ini. Secara opsional, layani proyek dan ikuti kode untuk berkenalan dengan aplikasi yang ada di tangan.
Mari kita gambarkan aplikasi Pastebin kita sebentar. Aplikasi ini awalnya akan memuat daftar pastes (diambil dari server tiruan) ke dalam tabel. Setiap baris dalam tabel akan memiliki tombol View Paste yang, ketika diklik, membuka jendela modal bootstrap. Jendela modal menampilkan data paste dengan opsi untuk mengedit dan menghapus paste. Menjelang ujung meja, ada tombol Create Paste yang dapat digunakan untuk menambahkan pastes baru.



Sisa dari tutorial ini didedikasikan untuk menulis tes Protractor di Angular.
Dasar Protractor
File spek, yang diakhiri dengan .e2e-spec.ts, akan menghosting tes yang sebenarnya untuk aplikasi kita. Kami akan menempatkan semua spesifikasi pengujian di dalam direktori e2e karena itulah tempat kami mengkonfigurasi Protractor untuk mencari spesifikasi.
Ada dua hal yang perlu Anda pertimbangkan saat menulis tes Protractor:
- Sintaks Jasmine
- Protractor API
Sintaks Jasmine
Buat file baru bernama test.e2e-spec.ts dengan kode berikut untuk memulai.
1 |
/* Path: e2e/test.e2e-spec.ts */
|
2 |
|
3 |
import { browser, by, element } from 'protractor'; |
4 |
|
5 |
describe('Protractor Demo', () => { |
6 |
|
7 |
beforeEach(() => { |
8 |
//The code here will get executed before each it block is called
|
9 |
//browser.get('/');
|
10 |
});
|
11 |
|
12 |
it('should display the name of the application',() => { |
13 |
/*Expectations accept parameters that will be matched with the real value
|
14 |
using Jasmine's matcher functions. eg. toEqual(),toContain(), toBe(), toBeTruthy() etc. */
|
15 |
expect("Pastebin Application").toEqual("Pastebin Application"); |
16 |
|
17 |
});
|
18 |
|
19 |
it('should click the create Paste button',() => { |
20 |
//spec goes here
|
21 |
|
22 |
});
|
23 |
});
|
Ini menggambarkan bagaimana pengujian kami akan diatur di dalam file spesifikasi menggunakan sintaks Jasmine. describe()
, beforeEach()
dan it()
adalah fungsi Jasmine global.
Jasmine memiliki sintaks yang bagus untuk menulis tes, dan bekerja sama dengan Protractor. Jika Anda baru mengenal Jasmine, saya akan merekomendasikan untuk menelusuri halaman GitHub Jasmine terlebih dahulu.
describe blok digunakan untuk membagi tes menjadi rangkaian uji logis. Setiap describe blok (atau rangkaian tes) dapat memiliki it beberapa blok (atau uji spesifikasi). Tes yang sebenarnya didefinisikan di dalam spesifikasi tes.
"Mengapa saya harus menyusun tes saya dengan cara ini?" Anda mungkin bertanya. Sebuah rangkaian tes dapat digunakan untuk secara logis menggambarkan fitur tertentu dari aplikasi Anda. Misalnya, semua spesifikasi yang berkaitan dengan komponen Pastebin idealnya harus dibahas di dalam blok mendeskripsikan berjudul Halaman Pastebin. Meskipun ini dapat menghasilkan tes yang berlebihan, tes Anda akan lebih mudah dibaca dan dipelihara.
Blok mendeskripsikan dapat memiliki metode beforeEach()
yang akan dijalankan satu kali, sebelum setiap spesifikasi di blok itu. Jadi, jika Anda perlu browser untuk menavigasi ke URL sebelum setiap tes, menempatkan kode untuk navigasi di dalam beforeEach()
adalah hal yang benar untuk dilakukan.
Ekspektasikan pernyataan, yang menerima nilai, dirangkai dengan beberapa fungsi matcher. Baik nilai nyata dan nilai yang diharapkan dibandingkan, dan boolean dikembalikan yang menentukan apakah pengujian gagal atau tidak.
Protractor API
Sekarang, mari taruh daging di atasnya.
1 |
/* Path: e2e/test.e2e-spec.ts */
|
2 |
|
3 |
import { browser, by, element } from 'protractor'; |
4 |
|
5 |
describe('Protractor Demo', () => { |
6 |
|
7 |
beforeEach(() => { |
8 |
browser.get('/'); |
9 |
});
|
10 |
|
11 |
it('should display the name of the application',() => { |
12 |
|
13 |
expect(element(by.css('.pastebin')).getText()).toContain('Pastebin Application'); |
14 |
|
15 |
});
|
16 |
|
17 |
it('create Paste button should work',() => { |
18 |
|
19 |
expect(element(by.id('source-modal')).isPresent()).toBeFalsy("The modal window shouldn't appear right now "); |
20 |
element(by.buttonText('create Paste')).click(); |
21 |
expect(element(by.id('source-modal')).isPresent()).toBeTruthy('The modal window should appear now'); |
22 |
|
23 |
});
|
24 |
});
|
browser.get('/')
dan element(by.css('.pastebin')).getText()
adalah bagian dari Protractor API. Mari dapati tangan kita kotor dan melompat tepat ke apa yang ditawarkan Protractor.
Komponen utama yang diekspor oleh Protractor API tercantum di bawah ini.
-
browser()
: Anda harus memanggilbrowser()
untuk semua operasi tingkat browser seperti navigasi, debugging, dll. -
element()
: Ini digunakan untuk mencari elemen di DOM berdasarkan kondisi pencarian atau rantai kondisi. Ini mengembalikan objek ElementFinder, dan Anda dapat melakukan tindakan sepertigetText()
atauclick()
pada mereka. -
element.all()
: Ini digunakan untuk mencari berbagai elemen yang cocok dengan beberapa rantai kondisi. Ini mengembalikan objek ElementArrayFinder. Semua tindakan yang dapat dilakukan pada ElementFinder dapat dilakukan pada ElementArrayFinder juga. - locators: Locators menyediakan metode untuk menemukan elemen dalam aplikasi Angular.
Karena kami akan menggunakan pencari sangat sering, berikut adalah beberapa pencari yang umum digunakan.
-
by.css('selector-name')
: Ini sejauh ini adalah pencari yang umum digunakan untuk menemukan elemen berdasarkan nama selector CSS. -
by.name('name-value')
: Menempatkan elemen dengan nilai yang cocok untuk atribut nama. -
by.buttonText('button-value')
: Menempatkan elemen tombol atau array elemen tombol berdasarkan teks bagian dalam.
Catatan: Locators by.model, by.binding dan by.repeater tidak bekerja dengan aplikasi Angular 2+ pada saat menulis tutorial ini. Gunakan pencari berbasis CSS sebagai gantinya.
Mari tulis lebih banyak tes untuk aplikasi Pastebin kami.
1 |
it('should accept and save input values', () => { |
2 |
element(by.buttonText('create Paste')).click(); |
3 |
|
4 |
//send input values to the form using sendKeys
|
5 |
|
6 |
element(by.name('title')).sendKeys('Hello world in Ruby'); |
7 |
element(by.name('language')).element(by.cssContainingText('option', 'Ruby')).click(); |
8 |
element(by.name('paste')).sendKeys("puts 'Hello world';"); |
9 |
|
10 |
element(by.buttonText('Save')).click(); |
11 |
|
12 |
//expect the table to contain the new paste
|
13 |
const lastRow = element.all(by.tagName('tr')).last(); |
14 |
expect(lastRow.getText()).toContain("Hello world in Ruby"); |
15 |
});
|
Kode di atas berfungsi, dan Anda dapat memverifikasi sendiri. Namun, tidakkah Anda merasa lebih nyaman menulis tes tanpa kosakata Protractor-spesifik dalam file spesifikasi Anda? Inilah yang saya bicarakan:
1 |
it('should have an Create Paste button and modal window', () => { |
2 |
|
3 |
expect(addPastePage.isCreateButtonPresent()).toBeTruthy("The button should exist"); |
4 |
expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy("The modal window shouldn't appear, not yet!"); |
5 |
|
6 |
addPastePage.clickCreateButton(); |
7 |
|
8 |
expect(addPastePage.isCreatePasteModalPresent()).toBeTruthy("The modal window should appear now"); |
9 |
|
10 |
|
11 |
});
|
12 |
|
13 |
it('should accept and save input values', () => { |
14 |
|
15 |
addPastePage.clickCreateButton(); |
16 |
|
17 |
//Input field should be empty initially
|
18 |
const emptyInputValues = ["","",""]; |
19 |
expect(addPastePage.getInputPasteValues()).toEqual(emptyInputValues); |
20 |
|
21 |
//Now update the input fields
|
22 |
addPastePage.addNewPaste(); |
23 |
|
24 |
addPastePage.clickSaveButton(); |
25 |
|
26 |
expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy("The modal window should be gone"); |
27 |
expect(mainPage.getLastRowData()).toContain("Hello World in Ruby"); |
28 |
|
29 |
});
|
Spesifikasi tampak lebih jelas tanpa bagasi Protractor ekstra. Bagaimana saya melakukannya? Izinkan saya memperkenalkan Anda ke Halaman Objek.
Halaman Objek
Halaman Objek adalah pola desain yang populer di lingkaran otomatisasi pengujian. Sebuah objek halaman memodelkan suatu halaman atau bagian dari aplikasi yang menggunakan kelas berorientasi objek. Semua objek (yang relevan dengan pengujian kami) seperti teks, judul, tabel, tombol, dan tautan dapat ditangkap dalam objek halaman. Kami kemudian dapat mengimpor objek halaman ini ke dalam file spesifikasi dan menjalankan metode mereka. Ini mengurangi duplikasi kode dan membuat pemeliharaan kode lebih mudah.
Buat direktori bernama page-objects dan tambahkan file baru di dalamnya yang disebut pastebin.po.ts. Semua objek yang berkaitan dengan komponen Pastebin akan ditangkap di sini. Seperti yang disebutkan sebelumnya, kami membagi seluruh aplikasi menjadi tiga komponen yang berbeda, dan setiap komponen akan memiliki objek halaman yang didedikasikan untuk itu. Skema penamaan .po.ts adalah murni konvensional, dan Anda dapat menamainya apa pun yang Anda inginkan.
Ini adalah cetak biru dari halaman yang sedang kami uji.



Ini kodenya.
pastebin.po.ts
1 |
/* Path e2e/page-objects/pastebin.po.ts*/
|
2 |
|
3 |
import { browser, by, element, promise, ElementFinder, ElementArrayFinder } from 'protractor'; |
4 |
|
5 |
|
6 |
export class Pastebin extends Base { |
7 |
|
8 |
navigateToHome():promise.Promise<any> { |
9 |
return browser.get('/'); |
10 |
}
|
11 |
|
12 |
getPastebin():ElementFinder { |
13 |
return element(by.css('.pastebin')); |
14 |
}
|
15 |
|
16 |
/* Pastebin Heading */
|
17 |
getPastebinHeading(): promise.Promise<string> { |
18 |
return this.getPastebin().element(by.css("h2")).getText(); |
19 |
}
|
20 |
|
21 |
/*Table Data */
|
22 |
|
23 |
getTable():ElementFinder { |
24 |
return this.getTable().element(by.css('table')); |
25 |
|
26 |
}
|
27 |
|
28 |
getTableHeader(): promise.Promise<string> { |
29 |
return this.getPastebin().all(by.tagName('tr')).get(0).getText(); |
30 |
}
|
31 |
|
32 |
getTableRow(): ElementArrayFinder { |
33 |
return this.getPastebin().all(by.tagName('tr')); |
34 |
}
|
35 |
|
36 |
|
37 |
getFirstRowData(): promise.Promise<string> { |
38 |
return this.getTableRow().get(1).getText(); |
39 |
}
|
40 |
|
41 |
getLastRowData(): promise.Promise<string> { |
42 |
return this.getTableRow().last().getText(); |
43 |
}
|
44 |
|
45 |
/*app-add-paste tag*/
|
46 |
|
47 |
getAddPasteTag(): ElementFinder { |
48 |
return this.getPastebin().element(by.tagName('app-add-paste')); |
49 |
}
|
50 |
|
51 |
isAddPasteTagPresent(): promise.Promise<boolean> { |
52 |
return this.getAddPasteTag().isPresent(); |
53 |
}
|
54 |
|
55 |
}
|
Mari kita bahas apa yang telah kita pelajari sejauh ini. API Protractor mengembalikan objek, dan kami telah menemukan tiga jenis objek sejauh ini. Mereka:
- promise.Promise
- ElementFinder
- ElementArrayFinder
Singkatnya, element()
mengembalikan ElementFinder, dan element().all
mengembalikan ElementArrayFinder. Anda dapat menggunakan locators (by.css
, by.tagName
, dll.) Untuk menemukan lokasi elemen di DOM dan meneruskannya ke element()
atau element.all()
.
ElementFinder dan ElementArrayFinder kemudian dapat dikaitkan dengan tindakan, seperti isPresent()
, getText()
, click()
, dll. Metode-metode ini mengembalikan promise yang akan diselesaikan ketika tindakan tertentu telah selesai.
Alasan mengapa kita tidak memiliki rantai then()
dalam pengujian kami adalah karena Protractor mengurusnya secara internal. Tes tampak sinkron meskipun tidak; oleh karena itu, hasil akhirnya adalah pengalaman pengkodean linier. Namun, saya sarankan menggunakan sintaks async/await untuk memastikan bahwa kode tersebut adalah bukti masa depan.
Anda dapat menukar beberapa objek ElementFinder
, seperti yang ditunjukkan di bawah ini. Ini sangat membantu jika DOM memiliki banyak pemilih dengan nama yang sama dan kita harus menangkap yang benar.
1 |
getTable():ElementFinder { |
2 |
return this.getPastebin().element(by.css('table')); |
3 |
|
4 |
}
|
Sekarang kita memiliki kode untuk objek halaman siap, mari kita impor ke spec kami. Ini kode untuk tes awal kami.
1 |
/* Path: e2e/mainPage.e2e-spec.ts */
|
2 |
|
3 |
import { Pastebin } from './page-objects/pastebin.po'; |
4 |
import { browser, protractor } from 'protractor'; |
5 |
|
6 |
|
7 |
/* Scenarios to be Tested
|
8 |
1. Pastebin Page should display a heading with text Pastebin Application
|
9 |
2. It should have a table header
|
10 |
3. The table should have rows
|
11 |
4. app-add-paste tag should exist
|
12 |
*/
|
13 |
|
14 |
describe('Pastebin Page', () => { |
15 |
|
16 |
const mainPage: Pastebin = new Pastebin(); |
17 |
|
18 |
beforeEach(() => { |
19 |
mainPage.navigateToHome(); |
20 |
});
|
21 |
|
22 |
it('should display the heading Pastebin Application', () => { |
23 |
|
24 |
expect(mainPage.getPastebinHeading()).toEqual("Pastebin Application"); |
25 |
|
26 |
|
27 |
});
|
28 |
|
29 |
it('should have a table header', () => { |
30 |
|
31 |
expect(mainPage.getTableHeader()).toContain("id Title Language Code"); |
32 |
|
33 |
})
|
34 |
it('table should have at least one row', () => { |
35 |
|
36 |
expect(mainPage.getFirstRowData()).toContain("Hello world"); |
37 |
})
|
38 |
|
39 |
it('should have the app-add-paste tag', () => { |
40 |
expect(mainPage.isAddPasteTagPresent()).toBeTruthy(); |
41 |
})
|
42 |
});
|
Mengorganisir Tes dan Refactoring
Tes harus diatur sedemikian rupa sehingga struktur keseluruhan tampak bermakna dan lugas. Berikut adalah beberapa panduan yang dianjurkan yang harus Anda ingat saat mengatur tes E2E.
- Tes E2E terpisah dari tes unit.
- Kelompokkan tes E2E Anda dengan bijaksana. Atur tes Anda dengan cara yang sesuai dengan struktur proyek Anda.
- Jika ada beberapa halaman, objek halaman harus memiliki direktori terpisah sendiri.
- Jika objek halaman memiliki beberapa metode yang sama (seperti
navigasiToHome()
), buat objek halaman dasar. Model halaman lain dapat mewarisi dari model halaman dasar. - Buat tes Anda independen satu sama lain. Anda tidak ingin semua tes Anda gagal karena perubahan kecil di UI, bukan?
- Jaga definisi objek halaman bebas dari assertions/expectations. Pernyataan harus dibuat di dalam file spesifikasi.
Mengikuti panduan di atas, inilah hirarki objek halaman dan organisasi file yang akan terlihat.



Kami sudah membahas pastebin.po.ts dan mainPage.e2e-spec.ts. Berikut ini file-file sisanya.
Objek Halaman Dasar
1 |
/* path: e2e/page-objects/base.po.ts */
|
2 |
|
3 |
import { browser, by, element, promise, ElementFinder, ElementArrayFinder } from 'protractor'; |
4 |
|
5 |
export class Base { |
6 |
|
7 |
/* Navigational methods */
|
8 |
navigateToHome():promise.Promise<any> { |
9 |
return browser.get('/'); |
10 |
}
|
11 |
|
12 |
navigateToAbout():promise.Promise<any> { |
13 |
return browser.get('/about'); |
14 |
}
|
15 |
|
16 |
navigateToContact():promise.Promise<any> { |
17 |
return browser.get('/contact'); |
18 |
}
|
19 |
|
20 |
/* Mock data for creating a new Paste and editing existing paste */
|
21 |
|
22 |
getMockPaste(): any { |
23 |
let paste: any = { title: "Something here",language: "Ruby",paste: "Test"} |
24 |
return paste; |
25 |
}
|
26 |
|
27 |
getEditedMockPaste(): any { |
28 |
let paste: any = { title: "Paste 2", language: "JavaScript", paste: "Test2" } |
29 |
return paste; |
30 |
}
|
31 |
|
32 |
/* Methods shared by addPaste and viewPaste */
|
33 |
|
34 |
getInputTitle():ElementFinder { |
35 |
return element(by.name("title")); |
36 |
}
|
37 |
|
38 |
getInputLanguage(): ElementFinder { |
39 |
return element(by.name("language")); |
40 |
}
|
41 |
|
42 |
getInputPaste(): ElementFinder { |
43 |
return element(by.name("paste")); |
44 |
|
45 |
}
|
46 |
}
|
Tambahkan Paste Objek Halaman



1 |
/* Path: e2e/page-objects/add-paste.po.ts */
|
2 |
|
3 |
import { browser, by, element, promise, ElementFinder, ElementArrayFinder } from 'protractor'; |
4 |
import { Base } from './base.po'; |
5 |
export class AddPaste extends Base { |
6 |
|
7 |
getAddPaste():ElementFinder { |
8 |
return element(by.tagName('app-add-paste')); |
9 |
}
|
10 |
|
11 |
/* Create Paste button */
|
12 |
getCreateButton(): ElementFinder { |
13 |
return this.getAddPaste().element(by.buttonText("create Paste")); |
14 |
}
|
15 |
|
16 |
isCreateButtonPresent() : promise.Promise<boolean> { |
17 |
return this.getCreateButton().isPresent(); |
18 |
}
|
19 |
|
20 |
clickCreateButton(): promise.Promise<void> { |
21 |
return this.getCreateButton().click(); |
22 |
}
|
23 |
|
24 |
/*Create Paste Modal */
|
25 |
|
26 |
getCreatePasteModal(): ElementFinder { |
27 |
return this.getAddPaste().element(by.id("source-modal")); |
28 |
}
|
29 |
|
30 |
isCreatePasteModalPresent() : promise.Promise<boolean> { |
31 |
return this.getCreatePasteModal().isPresent(); |
32 |
}
|
33 |
|
34 |
/*Save button */
|
35 |
getSaveButton(): ElementFinder { |
36 |
return this.getAddPaste().element(by.buttonText("Save")); |
37 |
}
|
38 |
|
39 |
clickSaveButton():promise.Promise<void> { |
40 |
return this.getSaveButton().click(); |
41 |
}
|
42 |
|
43 |
/*Close button */
|
44 |
|
45 |
getCloseButton(): ElementFinder { |
46 |
return this.getAddPaste().element(by.buttonText("Close")); |
47 |
}
|
48 |
|
49 |
clickCloseButton():promise.Promise<void> { |
50 |
return this.getCloseButton().click(); |
51 |
}
|
52 |
|
53 |
|
54 |
/* Get Input Paste values from the Modal window */
|
55 |
getInputPasteValues(): Promise<string[]> { |
56 |
let inputTitle, inputLanguage, inputPaste; |
57 |
|
58 |
// Return the input values after the promise is resolved
|
59 |
// Note that this.getInputTitle().getText doesn't work
|
60 |
// Use getAttribute('value') instead
|
61 |
return Promise.all([this.getInputTitle().getAttribute("value"), this.getInputLanguage().getAttribute("value"), this.getInputPaste().getAttribute("value")]) |
62 |
.then( (values) => { |
63 |
return values; |
64 |
});
|
65 |
|
66 |
}
|
67 |
|
68 |
/* Add a new Paste */
|
69 |
|
70 |
addNewPaste():any { |
71 |
let newPaste: any = this.getMockPaste(); |
72 |
|
73 |
//Send input values
|
74 |
this.getInputTitle().sendKeys(newPaste.title); |
75 |
this.getInputLanguage() |
76 |
.element(by.cssContainingText('option', newPaste.language)).click(); |
77 |
this.getInputPaste().sendKeys(newPaste.paste); |
78 |
|
79 |
//Convert the paste object into an array
|
80 |
return Object.keys(newPaste).map(key => newPaste[key]); |
81 |
|
82 |
}
|
83 |
|
84 |
}
|
Tambahkan file Paste Spec
1 |
/* Path: e2e/addNewPaste.e2e-spec.ts */
|
2 |
|
3 |
import { Pastebin } from './page-objects/pastebin.po'; |
4 |
import { AddPaste } from './page-objects/add-paste.po'; |
5 |
import { browser, protractor } from 'protractor'; |
6 |
|
7 |
/* Scenarios to be Tested
|
8 |
1. AddPaste Page should have a button when clicked on should present a modal window
|
9 |
2. The modal window should accept the new values and save them
|
10 |
4. The saved data should appear in the MainPage
|
11 |
3. Close button should work
|
12 |
*/
|
13 |
|
14 |
describe('Add-New-Paste page', () => { |
15 |
|
16 |
const addPastePage: AddPaste = new AddPaste(); |
17 |
const mainPage: Pastebin = new Pastebin(); |
18 |
|
19 |
beforeEach(() => { |
20 |
|
21 |
addPastePage.navigateToHome(); |
22 |
});
|
23 |
|
24 |
it('should have an Create Paste button and modal window', () => { |
25 |
|
26 |
expect(addPastePage.isCreateButtonPresent()).toBeTruthy("The button should exist"); |
27 |
expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy("The modal window shouldn't appear, not yet!"); |
28 |
|
29 |
addPastePage.clickCreateButton(); |
30 |
|
31 |
expect(addPastePage.isCreatePasteModalPresent()).toBeTruthy("The modal window should appear now"); |
32 |
|
33 |
|
34 |
});
|
35 |
|
36 |
it("should accept and save input values", () => { |
37 |
|
38 |
addPastePage.clickCreateButton(); |
39 |
|
40 |
const emptyInputValues = ["","",""]; |
41 |
expect(addPastePage.getInputPasteValues()).toEqual(emptyInputValues); |
42 |
|
43 |
const newInputValues = addPastePage.addNewPaste(); |
44 |
expect(addPastePage.getInputPasteValues()).toEqual(newInputValues); |
45 |
|
46 |
addPastePage.clickSaveButton(); |
47 |
|
48 |
expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy("The modal window should be gone"); |
49 |
expect(mainPage.getLastRowData()).toContain("Something here"); |
50 |
|
51 |
});
|
52 |
|
53 |
it("close button should work", () => { |
54 |
|
55 |
addPastePage.clickCreateButton(); |
56 |
addPastePage.clickCloseButton(); |
57 |
|
58 |
expect(addPastePage.isCreatePasteModalPresent()).toBeFalsy("The modal window should be gone"); |
59 |
|
60 |
});
|
61 |
|
62 |
});
|
Latihan
Ada beberapa hal yang hilang, meskipun: tes untuk tombol View Paste dan jendela modal yang muncul setelah mengklik tombol. Saya akan meninggalkan ini sebagai latihan untuk Anda. Namun, saya akan memberi Anda petunjuk.
Struktur objek halaman dan spesifikasi untuk ViewPastePage mirip dengan yang ada pada AddPastePage.



Berikut adalah skenario yang perlu Anda uji:
- Halaman ViewPaste harus memiliki sebuah tombol, dan pada klik, itu akan memunculkan jendela modal.
- Jendela modal harus menampilkan data tempel dari paste yang baru ditambahkan.
- Jendela modal harus membiarkan Anda memperbarui nilai.
- Tombol hapus seharusnya berfungsi.
Cobalah untuk tetap berpegang pada pedoman sedapat mungkin. Jika Anda ragu, alihkan ke cabang terakhir untuk melihat draf terakhir dari kode.
Membungkusnya
Jadi begitulah. Dalam artikel ini, kami telah membahas penulisan tes end-to-end untuk aplikasi Angular kami menggunakan Protractor. Kami memulai dengan diskusi tentang tes unit vs. tes e2e, dan kemudian kami belajar tentang pengaturan, konfigurasi dan menjalankan Protractor. Sisa dari tutorial berkonsentrasi pada penulisan tes yang sebenarnya untuk aplikasi demo Pastebin.
Tolong beri tahu saya pemikiran dan pengalaman Anda tentang menulis tes menggunakan Protractor atau tes tertulis untuk Angular secara umum. Saya akan senang mendengarnya. Terima kasih sudah membaca!