Ricerca…
Asserzione di base
Al suo livello più elementare, il test unitario in qualsiasi lingua fornisce asserzioni su alcuni risultati noti o previsti.
function assert( outcome, description ) {
var passFail = outcome ? 'pass' : 'fail';
console.log(passFail, ': ', description);
return outcome;
};
Il popolare metodo di asserzione sopra ci mostra un modo facile e veloce per affermare un valore nella maggior parte dei browser Web e degli interpreti come Node.js con praticamente qualsiasi versione di ECMAScript.
Un buon test unitario è progettato per testare un'unità discreta di codice; di solito una funzione.
function add(num1, num2) {
return num1 + num2;
}
var result = add(5, 20);
assert( result == 24, 'add(5, 20) should return 25...');
Nell'esempio sopra, il valore restituito dalla funzione add(x, y)
o 5 + 20
è chiaramente 25
, quindi la nostra asserzione di 24
dovrebbe fallire e il metodo asser registrerà una riga "fail".
Se modifichiamo semplicemente il risultato previsto per l'asserzione, il test avrà esito positivo e l'output risultante sarà simile a questo.
assert( result == 25, 'add(5, 20) should return 25...');
console output:
> pass: should return 25...
Questa semplice asserzione può assicurare che in molti casi diversi, la funzione "aggiungi" restituirà sempre il risultato previsto e non richiede strutture o librerie aggiuntive per funzionare.
Un set di asserzioni più rigoroso sarebbe simile a questo (usando var result = add(x,y)
per ogni asserzione):
assert( result == 0, 'add(0, 0) should return 0...');
assert( result == -1, 'add(0, -1) should return -1...');
assert( result == 1, 'add(0, 1) should return 1...');
E l'output della console sarebbe questo:
> pass: should return 0...
> pass: should return -1...
> pass: should return 1...
Ora possiamo tranquillamente dire che add(x,y)
... dovrebbe restituire la somma di due interi . Possiamo sistemarli in qualcosa del genere:
function test__addsIntegers() {
// expect a number of passed assertions
var passed = 3;
// number of assertions to be reduced and added as Booleans
var assertions = [
assert( add(0, 0) == 0, 'add(0, 0) should return 0...'),
assert( add(0, -1) == -1, 'add(0, -1) should return -1...'),
assert( add(0, 1) == 1, 'add(0, 1) should return 1...')
].reduce(function(previousValue, currentValue){
return previousValue + current;
});
if (assertions === passed) {
console.log("add(x,y)... did return the sum of two integers");
return true;
} else {
console.log("add(x,y)... does not reliably return the sum of two integers");
return false;
}
}
Promesse di unit test con Mocha, Sinon, Chai e Proxyquire
Qui abbiamo una semplice classe da testare che restituisce una Promise
basata sui risultati di un ResponseProcessor
esterno che richiede tempo per essere eseguito.
Per semplicità supponiamo che il metodo processResponse
non fallirà mai.
import {processResponse} from '../utils/response_processor';
const ping = () => {
return new Promise((resolve, _reject) => {
const response = processResponse(data);
resolve(response);
});
}
module.exports = ping;
Per testare questo possiamo sfruttare i seguenti strumenti.
Io uso il seguente script di test
nel mio file package.json
.
"test": "NODE_ENV=test mocha --compilers js:babel-core/register --require ./test/unit/test_helper.js --recursive test/**/*_spec.js"
Questo mi permette di usare la sintassi es6
. Fa riferimento a un test_helper
che assomiglierà
import chai from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import chaiAsPromised from 'chai-as-promised';
import sinonStubPromise from 'sinon-stub-promise';
chai.use(sinonChai);
chai.use(chaiAsPromised);
sinonStubPromise(sinon);
Proxyquire
ci consente di iniettare il nostro stub al posto del ResponseProcessor
esterno. Possiamo quindi usare sinon
per spiare i metodi di quello stub. Usiamo le estensioni chai
che gli inietti chai-as-promised
per verificare che la promessa del metodo ping()
sia fullfilled
e che eventually
restituisca la risposta richiesta.
import {expect} from 'chai';
import sinon from 'sinon';
import proxyquire from 'proxyquire';
let formattingStub = {
wrapResponse: () => {}
}
let ping = proxyquire('../../../src/api/ping', {
'../utils/formatting': formattingStub
});
describe('ping', () => {
let wrapResponseSpy, pingResult;
const response = 'some response';
beforeEach(() => {
wrapResponseSpy = sinon.stub(formattingStub, 'wrapResponse').returns(response);
pingResult = ping();
})
afterEach(() => {
formattingStub.wrapResponse.restore();
})
it('returns a fullfilled promise', () => {
expect(pingResult).to.be.fulfilled;
})
it('eventually returns the correct response', () => {
expect(pingResult).to.eventually.equal(response);
})
});
Ora invece supponiamo che desideri testare qualcosa che usi la risposta dal ping
.
import {ping} from './ping';
const pingWrapper = () => {
ping.then((response) => {
// do something with the response
});
}
module.exports = pingWrapper;
Per testare il pingWrapper
facciamo leva
Come prima, Proxyquire
ci consente di iniettare il nostro stub al posto della dipendenza esterna, in questo caso il metodo ping
che abbiamo testato in precedenza. Possiamo quindi usare sinon
per spiare i metodi di tale stub e sfruttare la sinon-stub-promise
per permetterci di returnsPromise
. Questa promessa può quindi essere risolta o respinta come desideriamo nel test, al fine di testare la risposta del wrapper a questo.
import {expect} from 'chai';
import sinon from 'sinon';
import proxyquire from 'proxyquire';
let pingStub = {
ping: () => {}
};
let pingWrapper = proxyquire('../src/pingWrapper', {
'./ping': pingStub
});
describe('pingWrapper', () => {
let pingSpy;
const response = 'some response';
beforeEach(() => {
pingSpy = sinon.stub(pingStub, 'ping').returnsPromise();
pingSpy.resolves(response);
pingWrapper();
});
afterEach(() => {
pingStub.wrapResponse.restore();
});
it('wraps the ping', () => {
expect(pingSpy).to.have.been.calledWith(response);
});
});