What we need?
Karma is a test runner, that makes automated tests simpler and faster
Architettura Client-Server con un canale di comunicazione bidirezionale tra client e server
Il server gira in ambiente nodejs sulla macchina locale, e si occupa di:
Architettura Client-Server con un canale di comunicazione bidirezionale tra client e server
Il client è il luogo in cui tutti test vengono eseguiti:
npm install karma --save-dev
Generazione del file di configurazione:
karma init
karma.config.js
module.exports = function(config) {
config.set({
basePath: '../..',
frameworks: ['jasmine'],
autoWatch : false,
browsers : [ 'PhantomJS' ]
});
};
Per lanciare i test:
karma start
Setup Test Suite e AngularJS
Client side package manager
Installazione bower:
npm install -g bower
# Creazione manifest
bower init
# Installazione package
bower install PACKAGE_NAME --save
# Ricerca package
bower search PATTERN
Utilizzo degli asset gestiti:
<script src="bower_components/jquery/dist/jquery.min.js"></script>
Metodi principali:
module and inject
var ShopCartService;
module('service.cart');
inject(function(_ShopCartService_){
ShopCartService = _ShopCartService_;
});
Servizi principali:
$httpBackend
beforeEach(inject(function(_$httpBackend_){
$httpBackend = _$httpBackend_;
newsPostsHandler = $httpBackend.when('GET', 'http://jsonplaceholder.typicode.com/posts');
}));
...
newsPostsHandler.respond(200, [{
id: 1,
title: "This is a title",
body: "This is a body"
}]);
Esercitazione 01 (Angularjs Testing)
Jasmine
describe
: identifica la suiteit
: identifica la specificaexpect
: identifica l'assertionHook globali:
beforeEach
afterEach
beforeAll
afterAll
Esercitazione 01 (Angularjs Testing)
Scriverne i test mancanti.
Soluzione (1/2)
it('sets the proper posts variable into the scope', function() {
newsPostsHandler.respond(200, [{
id: 1,
title: "This is a title",
body: "This is a body"
}]);
var $scope = {};
var controller = $controller('NewsPostController', { $scope: $scope });
expect($scope.loading).toBeTruthy();
$httpBackend.flush();
expect($scope.posts).toBeDefined();
expect($scope.loading).toBeFalsy();
expect($scope.posts[0].title).toBe("This is a title");
});
Soluzione (2/2)
it('sets the error post on error response', function() {
newsPostsHandler.respond(400, null, null, "Some errors occourred");
var $scope = {};
var controller = $controller('NewsPostController', { $scope: $scope });
expect($scope.loading).toBeTruthy();
$httpBackend.flush();
expect($scope.posts).toBeDefined();
expect($scope.loading).toBeFalsy();
expect($scope.posts[0].title).toBe("ERRORE");
expect($scope.posts[0].body).toBe("Some errors occourred");
});
Esercitazione 02 (Angularjs Testing)
Scriverne l'implementazione.
Soluzione
app.controller("NewsPhotoController", function($scope, $http) {
$scope.loading = true;
$http.get("http://jsonplaceholder.typicode.com/photos")
.then(function(res) {
$scope.loading = false;
if (res.status === 200) {
$scope.images = res.data;
}
});
});
Selenium: una suite di strumenti per il test e2e.
Il principio alla base si chiama "proxy injection"
#installazione protactor come modulo node globale
npm install -g protractor
#installazione di un'istanza completa di Selenium-Webdriver
webdriver-manager update
#avvio di Selenium
webdriver-manager start
È possibile ottenere informazioni sullo stato del server Selenium caricando l'URL http://localhost:4444/wd/hub
Protractor necessita di almeno due file per essere eseguito:
Lo spec file può essere scritto mediante il framework Jasmine, utilizzando le API specifiche di Protractor
describe('angularjs homepage todo list', function() {
it('should add a todo', function() {
//Caricamento applicazione
browser.get('<Application URL>');
//Selezione elemento della UI e invio testo di prova
element(by.model('list.todoText')).sendKeys('example text');
//Jasmine expectation
expect(...).toEqual(...);
});
});
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['todo-spec.js']
};
Avvio test E2E:
protractor conf.js
Reference: https://github.com/.../referenceConf.js
Le funzionalità base di una suite di test E2E per applicazioni WEB sono:
Le suite di test E2E sono caratterizzate, come per i test unit dalle seguenti fasi:
Protractor istanzia la variabile globale browser per esporre le funzionalità browser-level:
Nota: per manipolare gruppi di elementi occorre utilizzare la funzione element.all
Lo scopo di un locator è indicare il modo in cui un elemento del DOM deve essere recuperato:
// find an element using a css selector
by.css('.myclass')
// find an element with the given id
by.id('myid')
// find an element with a certain ng-model
by.model('name')
// find an element bound to the given variable
by.binding('bindingname')
È possibile incatenare le funzioni element ed element.all:
// find sub.element
element(by.css('some-css')).element(by.tagName('tag-within-css'));
// find a list of sub.elements
element(by.css('some-css')).all(by.tagName('tag-within-css'));
// get/first/last
element.all(by.css('css')).first().element(by.tagName('tag'));
element.all(by.css('css')).get(index).element(by.tagName('tag'));
element.all(by.css('css')).last().element(by.tagName('tag'));
L'oggetto ElementFinder contatta il browser attraverso un comando Selenium nel momento in cui viene invocata una action:
// Click on the element
el.click();
// Send keys to the element (usually an input)
el.sendKeys('my text');
// Clear the text in an element (usually an input)
el.clear();
// Get the value of an attribute
el.getAttribute('value');
Sono supportate tutte le action esposte da WebDriverJS
Tutte le actions sono metodi asincroni e ritornano un oggetto promise:
var el = element(locator);
el.getText().then(function(text) {
console.log(text);
});
È possibile utilizzare i matchers jasmine in chaining insieme a chiamate asincrone protractor:
expect(el.getText()).toBe('Hello, World!')
Invece di:
el.getText().then(function(text) {
expect(text).toBe('Hello, World!');
});
Esercitazione 03 (E2E tests)
Esercitazione 03 (E2E tests)
Completare il test e2e mancante
Soluzione
it("should add the same product to the cart for n times", function() {
var n = 4;
for (var i = 0; i < n; i++) {
page.addProductToCart(0);
}
expect(page.cartItems.count()).toEqual(1);
expect(page.getProduct(0).title).toBe(page.getCartItem(0).title);
expect(page.getCartItem(0).quantity).toBe("x" + n);
});
from martinfowler.com
Alca Società Cooperativa http://alcacoop.it
Released under CC BY-NC-SA 3.0