[ English | 中文 (简体, 中国) | русский | português (Brasil) | नेपाली | 한국어 (대한민국) | Indonesia | français | español | esperanto | English (United Kingdom) | Deutsch ]
Pengujian JavaScript¶
Ada beberapa komponen dalam kerangka pengujian JavaScript kami:
Jasmine adalah kerangka pengujian kami, jadi ini mendefinisikan sintaks dan struktur file yang kami gunakan untuk menguji JavaScript kami.
Karma adalah test runner kami. Di antara hal-hal lain, ini memungkinkan kita menjalankan pengujian terhadap banyak browser dan menghasilkan laporan cakupan pengujian. Atau, tes dapat dijalankan di dalam browser dengan Jasmine spec runner.
PhantomJS memberikan headless WebKit (mesin browser). Ini memberi kami dukungan asli untuk banyak fitur web tanpa bergantung pada browser tertentu yang sedang diinstal.
ESLint adalah utilitas linting kode pluggable. Ini akan menangkap kesalahan kecil dan inkonsistensi dalam JS Anda, yang dapat menyebabkan masalah yang lebih besar di kemudian hari. Lihat :ref: js_code_style untuk detail lebih lanjut.
Jasmine menggunakan spesifikasi (.spec.js
) yang disimpan dengan file JavaScript yang mereka uji. Lihat bagian :ref: js_file_structure atau Examples di bawah untuk detail lebih lanjut tentang ini.
Running Tests¶
Test dapat dijalankan dengan dua cara:
Buka <dev_server_ip:port>/jasmine di browser. Server pengembangan dapat dijalankan dengan
tox -e runserver
dari direktori root horizon.tox -e npm
dari direktori root horizon. Ini menjalankan Karma, sehingga akan menjalankan semua tes terhadap PhantomJS dan menghasilkan laporan liputan (coverage report).
Pekerjaan kode linting dapat dijalankan dengan tox -e npm -- lint
, atau tox -e npm -- lintq
untuk menampilkan kesalahan, tetapi tidak dengan peringatan.
Untuk menguraikan di mana tes gagal, mungkin berguna untuk menggunakan Jasmine di browser untuk menjalankan tes individu untuk melihat di mana tes secara khusus melanggar. Untuk melakukan ini, navigasikan ke horizon lokal Anda di browser dan tambahkan '/jasmine' di akhir url. misal 'http://localhost:8000/jasmine'. Setelah Anda memiliki laporan jasmine, Anda dapat mengklik judul tes individu untuk menjalankan kembali tes itu. Dari sini, Anda juga dapat menggunakan chrome dev tool atau sejenisnya untuk mengatur breakpoint dalam kode dengan mengakses tab 'Sources' dan mengklik pada baris kode di mana Anda ingin memecahkan kode. Ini kemudian akan menunjukkan tempat yang tepat di mana kode rusak.
Coverage Reports (laporan liputan)¶
Pengaturan Karma kami mencakup plugin untuk menghasilkan laporan cakupan pengujian. Saat mengembangkan, pastikan untuk memeriksa laporan cakupan pada master branch dan membandingkan development branch Anda; ini akan membantu mengidentifikasi tes yang hilang.
Untuk menghasilkan laporan cakupan, jalankan tox -e npm
. Laporan liputan dapat ditemukan di cover/horizon/
(framework tests) dan cover/openstack_dashboard/
(dashboard tests). Load <browser>/index.html
di browser untuk melihat laporan.
Writing Tests¶
Jasmine menggunakan suite dan spec:
Suite dimulai dengan panggilan ke
describe
, yang mengambil dua parameter; string dan fungsi. String adalah nama atau judul untuk suite spec, sementara fungsi adalah blok yang mengimplementasikan suite.Spec dimulai dengan panggilan ke
it
, yang juga menggunakan string dan fungsi sebagai parameter. String adalah nama atau judul, sementara fungsinya adalah blok dengan satu atau lebih expectation (expect
) yang menguji status kode. Expectation di Jasmine adalah pernyataan yang benar atau salah; setiap expectation dalam sebuah spec harus benar untuk lulus spec.
File .spec.js
dapat ditangani secara manual atau otomatis. Untuk menggunakan penemuan file otomatis, tambahkan:
AUTO_DISCOVER_STATIC_FILES = True
ke file yang diaktifkan. Kode JS untuk pengujian harus menggunakan ekstensi .mock.js
dan .spec.js
.
Anda dapat membaca lebih lanjut tentang fungsi di :ref: auto_discover_static_files dari dokumentasi pengaturan.
Untuk menambahkan spesifikasi secara manual, tambahkan array berikut dan jalur file yang relevan ke file yang diaktifkan:
ADD_JS_SPEC_FILES = [
...
'path_to/my_angular_code.spec.js',
...
]
Contoh¶
Catatan
Kode di bawah ini hanya untuk tujuan contoh, dan mungkin tidak berlaku saat ini. Ellipses (...) digunakan untuk mewakili kode yang telah dihapus demi singkatnya.
Contoh 1 - Komponen yang dapat digunakan kembali dalam direktori horizon¶
Tree file:
horizon/static/framework/widgets/modal
├── modal.controller.js
├── modal.module.js
├── modal.service.js
└── modal.spec.js
Baris ditambahkan ke horizon/test/jasmine/jasmine_tests.py
:
class ServicesTests(test.JasmineTests):
sources = [
...
'framework/widgets/modal/modal.module.js',
'framework/widgets/modal/modal.controller.js',
'framework/widgets/modal/modal.service.js',
...
]
specs = [
...
'framework/widgets/modal/modal.spec.js',
...
]
modal.spec.js
:
...
(function() {
"use strict";
describe('horizon.framework.widgets.modal module', function() {
beforeEach(module('horizon.framework'));
describe('simpleModalCtrl', function() {
var scope;
var modalInstance;
var context;
var ctrl;
beforeEach(inject(function($controller) {
scope = {};
modalInstance = {
close: function() {},
dismiss: function() {}
};
context = { what: 'is it' };
ctrl = $controller('simpleModalCtrl', {
$scope: scope,
$modalInstance: modalInstance,
context: context
});
}));
it('establishes a controller', function() {
expect(ctrl).toBeDefined();
});
it('sets context on the scope', function() {
expect(scope.context).toBeDefined();
expect(scope.context).toEqual({ what: 'is it' });
});
it('sets action functions', function() {
expect(scope.submit).toBeDefined();
expect(scope.cancel).toBeDefined();
});
it('makes submit close the modal instance', function() {
expect(scope.submit).toBeDefined();
spyOn(modalInstance, 'close');
scope.submit();
expect(modalInstance.close.calls.count()).toBe(1);
});
it('makes cancel close the modal instance', function() {
expect(scope.cancel).toBeDefined();
spyOn(modalInstance, 'dismiss');
scope.cancel();
expect(modalInstance.dismiss).toHaveBeenCalledWith('cancel');
});
});
...
});
})();
Contoh 2 - Kode khusus panel di direktori openstack_dashboard¶
Tree file:
openstack_dashboard/static/dashboard/launch-instance/network/
├── network.help.html
├── network.html
├── network.js
├── network.scss
└── network.spec.js
Baris ditambahkan ke openstack_dashboard/enabled/_10_project.py
:
LAUNCH_INST = 'dashboard/launch-instance/'
ADD_JS_FILES = [
...
LAUNCH_INST + 'network/network.js',
...
]
ADD_JS_SPEC_FILES = [
...
LAUNCH_INST + 'network/network.spec.js',
...
]
network.spec.js
:
...
(function(){
'use strict';
describe('Launch Instance Network Step', function() {
describe('LaunchInstanceNetworkCtrl', function() {
var scope;
var ctrl;
beforeEach(module('horizon.dashboard.project.workflow.launch-instance'));
beforeEach(inject(function($controller) {
scope = {
model: {
newInstanceSpec: {networks: ['net-a']},
networks: ['net-a', 'net-b']
}
};
ctrl = $controller('LaunchInstanceNetworkCtrl', {$scope:scope});
}));
it('has correct network statuses', function() {
expect(ctrl.networkStatuses).toBeDefined();
expect(ctrl.networkStatuses.ACTIVE).toBeDefined();
expect(ctrl.networkStatuses.DOWN).toBeDefined();
expect(Object.keys(ctrl.networkStatuses).length).toBe(2);
});
it('has correct network admin states', function() {
expect(ctrl.networkAdminStates).toBeDefined();
expect(ctrl.networkAdminStates.UP).toBeDefined();
expect(ctrl.networkAdminStates.DOWN).toBeDefined();
expect(Object.keys(ctrl.networkStatuses).length).toBe(2);
});
it('defines a multiple-allocation table', function() {
expect(ctrl.tableLimits).toBeDefined();
expect(ctrl.tableLimits.maxAllocation).toBe(-1);
});
it('contains its own labels', function() {
expect(ctrl.label).toBeDefined();
expect(Object.keys(ctrl.label).length).toBeGreaterThan(0);
});
it('contains help text for the table', function() {
expect(ctrl.tableHelpText).toBeDefined();
expect(ctrl.tableHelpText.allocHelpText).toBeDefined();
expect(ctrl.tableHelpText.availHelpText).toBeDefined();
});
it('uses scope to set table data', function() {
expect(ctrl.tableDataMulti).toBeDefined();
expect(ctrl.tableDataMulti.available).toEqual(['net-a', 'net-b']);
expect(ctrl.tableDataMulti.allocated).toEqual(['net-a']);
expect(ctrl.tableDataMulti.displayedAllocated).toEqual([]);
expect(ctrl.tableDataMulti.displayedAvailable).toEqual([]);
});
});
...
});
})();