Alca Società Cooperativa http://alcacoop.it
Released under CC BY-NC-SA 3.0
What we need?
What we need?
Behavior-driven development SYNTAX
describe
: identifica la suiteit
: identifica la specificaexpect
: identifica l'assertiondescribe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
Jasmine fornisce i seguenti hook globali:
beforeEach
afterEach
beforeAll
afterAll
expect(foo).toEqual(bar);
expect(true).not.toEqual(false);
Un matcher può valutare un'assertion negativa incatenando un not
alla chiamata ad expect
prima di chiamare il matcher
Included matchers
toBe
describe("the toBe Matcher", function() {
it("should compare both types and values", function() {
var actual = "123";
var expected = "123";
expect(actual).toBe(expected);
});
});
Included matchers
toEqual
describe("the toEqual Matcher", function() {
it("should be able to compare arrays", function() {
var actual = [1, 2, 3];
var expected = [1, 2, 3];
expect(actual).toEqual(expected);
});
});
Included matchers
toBeDefined and toBeUndefined
describe("the toBeDefined and toBeUndefined Matchers", function() {
it("should be able to check defined and undefined objects", function() {
var object1 = {prop1: "value1"};
var object2;
expect(object1).toBeDefined();
expect(object2).toBeUndefined();
expect(object2).not.toBeDefined();
});
});
Included matchers
toBeNull
describe("the toBeNull Matcher", function() {
it("should be able to check if an object value is null",
function() {
var object = null;
expect(object).toBeNull();
});
});
Included matchers
toContain
describe("the toContain Matcher", function() {
it("should check if a String contains a substring", function() {
expect("Hello World from Cairo").toContain("Cairo");
});
it("should check if an Array contains a specific item", function() {
expect(["TV", "Watch", "Table"]).toContain("Watch");
});
});
Included matchers
toBeLessThan and toBeGreatherThan
describe("the toBeLessThan Matcher", function() {
it("should be able to perform the less than operation", function() {
expect(4).toBeLessThan(5);
});
});
Included matchers
toMatch
describe("the toMatch Matcher", function() {
it("should be able to match the value with a regular expression",
function() {
expect("foo bar baz").toMatch(/bar/);
});
});
Custom matchers
var customMathers = {
// factory constructor
toBeMultipleOf: function (util, customEqualityTester) {
return {
compare: function(actual, expected) {
var result = {
pass: ((actual % expected) == 0)
}
if (!result.pass) {
result.message = "Expected " + actual +
" to be multiple of " + expected + ", but it isn't";
}
return result;
}
}
}
};
Insieme a compare
si può specificare negativeCompare
nel caso in cui l'utilizzo di not
si reputi insufficente.
Custom matchers...
beforeEach(function() {
jasmine.addMatchers(customMathers);
});
....
expect(6).toBeMultipleOf(3);
Eventuali criticità troppo complesse o importanti refactoring vanno considerati come debiti tecnici, da rimandare ad una successiva iterazione se la loro risoluzione mette a rischio l'esito dell'iterazione
La creazione di una suite di test automatizzata rende le fasi di refactoring, enhancement e manutenzione (correttiva e adattativa) molto confortevoli, assicurando la rapida scoperta di eventuali problemi di regressione
Ci fornisce una misura di quanto il nostro codice è stato coperto dalla suite di test creata.
Chiaramente più alto è il grado di copertura del codice più basse sono le possibilità che il software contenga bug.
Istanbul
Agisce in tre differenti fasi:
Code coverage reports: servono a farci un'idea di quanto codice è testato rispetto al totale scritto
Ogni test deve testare le singole unità in maniera indipendente dalle altre, in modo che se c'è un problema in una unità dovrebbe fallire solo il test relativo a tale unità e non gli altri.
I test doubles sono degli oggetti in grado di 'impersonare' sistemi di terze parti o componenti allo scopo del testing
Risolvono entrambi i problemi: isolation e test setup
Person.js
var Person = function() {};
Person.prototype.sayHello = function() {
return "Hello";
};
Person.prototype.helloSomeone = function(toGreet) {
return this.sayHello() + " " + toGreet;
};
Call a fake sayHello() method
it("calls the sayHello() dummy function", function() {
var fakePerson = new Person();
spyOn(fakePerson, "sayHello");
fakePerson.helloSomeone("world");
expect(fakePerson.sayHello).toHaveBeenCalled();
expect(fakePerson.helloSomeone("world")).toEqual("undefined world");
});
Call a fake sayHello() but propagete to the real sayHello()
it("calls the sayHello() real function but tracks the call", function() {
var fakePerson = new Person();
spyOn(fakePerson, "sayHello").and.callThrough();
fakePerson.helloSomeone("world");
expect(fakePerson.sayHello).toHaveBeenCalled();
expect(fakePerson.helloSomeone("world")).toEqual("Hello world");
});
Check passed-in parameters
it("greets the world", function() {
var fakePerson = new Person();
spyOn(fakePerson, "helloSomeone");
fakePerson.helloSomeone("world");
expect(fakePerson.helloSomeone).toHaveBeenCalledWith("world");
expect(fakePerson.helloSomeone).not.toHaveBeenCalledWith("foo");
});
Spy on _sayHello() and return a fixed value (stub)
it("says hello with fixed value", function() {
var fakePerson = new Person();
fakePerson.sayHello = jasmine.createSpy('"Say hello" spy')
.and.returnValue("Super hello");
fakePerson.helloSomeone("world");
expect(fakePerson.sayHello).toHaveBeenCalled();
expect(fakePerson.helloSomeone("world")).toEqual("Super hello world");
});
Spy on _sayHello() and call a fake function
it("says hello with a fake function", function() {
var fakePerson = new Person();
fakePerson.sayHello = jasmine.createSpy('"Say hello" spy')
.and.callFake(function() {
return "Buongiorno"
});
fakePerson.helloSomeone("mondo");
expect(fakePerson.sayHello).toHaveBeenCalled();
expect(fakePerson.helloSomeone("mondo")).toEqual("Buongiorno mondo");
});
Tipologie di test doubles:
Come scrivere codice di qualità e quindi testabile?
Rimuovere le dipendenze implicite e richiedere ad un oggetto (detto injector) di fornirci l'istanza corretta che utilizzeremo nel nostro componente come dipendenza.
Benefici sul codice:
le funzionalita' richieste spesso non corrispondono direttamente a singole classi, metodi e blocchi di codice
L'approccio BDD è essenzialmente una metodologia test-first in cui la specifica in test è visibile all'utente finale (o allo stakeholder)
Nel gergo BDD i test sono chiamati specifiche (specs)
Partire con test E2E (requisito minimo!), di solito espressi in BDD, a cui affiancare test di unità sulle componenti chiave:
Partire con test di unità sulle componenti sviluppate, a cui affiancare test E2E sull'intera architettura del sistema.
All'interno di ogni browser Web sono presenti dei Tool di Sviluppo integrati
I Tool di Sviluppo ci danno una visione privilegiata di alcuni dei meccanismi interni al browser Web
Selezionare dal "Menu Chrome
" nell'angolo in alto a destra della finestra del browser, e selezionare "Strumenti > Strumenti per lo sviluppatore" (o "Tools > Developer Tools")
oppure
Premere il tasto destro del mouse su un qualunque elemento nella pagina e selezionare "Ispeziona Elemento" (o "Inspect Element")
Selezionare dal "Menu Firefox
" nell'angolo in alto a destra della finestra del browser, e selezionare "Sviluppatore > Abilita/Disabilita Strumenti" (o "Developer > Toggle Developer Tools")
oppure
Premere il tasto destro del mouse su un qualunque elemento nella pagina e selezionare "Ispeziona Elemento" (o "Inspect Element")
Nel progetto ci sarà tipicamente un file package.json in cui descrivere ed elencare tutti i pacchetti npm di cui abbiamo bisogno come dipendenza
The Javascript task runner
npm install -g grunt-cli
"grunt-contrib-connect"
grunt.initConfig({
connect: {
server: {
options: {
port: 8000,
base: ".",
livereload: true,
open: true
}
}
}
});
grunt.loadNpmTasks( 'grunt-contrib-connect' );
grunt.registerTask( 'default', [ 'connect' ] );
"grunt-contrib-watch"
grunt.initConfig({
watch: {
options: {
livereload: true
},
html: {
files: [ "./src/**/*.html" ]
},
markdown: {
files: [ "./src/**/*.md" ]
}
}
});
grunt.loadNpmTasks( 'grunt-contrib-watch' );
grunt.registerTask( 'default', [ 'watch' ] );
The streaming build system
npm install -g grunt-cli
Run Mocha tests
var gulp = require('gulp');
var mocha = require('gulp-mocha');
var gutil = require('gulp-util');
gulp.task('mocha', function() {
return gulp.src(['test/*.js'], { read: false })
.pipe(mocha({ reporter: 'list' }))
.on('error', gutil.log);
});
gulp.task('watch-mocha', function() {
gulp.watch(['lib/**', 'test/**'], ['mocha']);
});
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
Alca Società Cooperativa http://alcacoop.it
Released under CC BY-NC-SA 3.0