Sök…
Grundläggande påstående
På sin mest grundläggande nivå ger enhetstestning på alla språk påståenden mot någon känd eller förväntad produktion.
function assert( outcome, description ) {
var passFail = outcome ? 'pass' : 'fail';
console.log(passFail, ': ', description);
return outcome;
};
Den populära påståendemetoden ovan visar oss ett snabbt och enkelt sätt att hävda ett värde i de flesta webbläsare och tolkar som Node.js med praktiskt taget vilken version av ECMAScript som helst.
Ett bra enhetstest är utformat för att testa en diskret enhet. vanligtvis en funktion.
function add(num1, num2) {
return num1 + num2;
}
var result = add(5, 20);
assert( result == 24, 'add(5, 20) should return 25...');
I exemplet ovan är returvärdet från funktionen add(x, y)
eller 5 + 20
tydligt 25
, så vår påstående om 24
bör misslyckas och påståttmetoden loggar en "fail" -rad.
Om vi helt enkelt modifierar vårt förväntade uttalande, kommer testet att lyckas och den resulterande produktionen ser ut så här.
assert( result == 25, 'add(5, 20) should return 25...');
console output:
> pass: should return 25...
Denna enkla påstående kan säkerställa att i många olika fall kommer din "add" -funktion alltid att returnera det förväntade resultatet och kräver inga ytterligare ramverk eller bibliotek för att fungera.
En strängare uppsättning påståenden skulle se ut så här (med var result = add(x,y)
för varje påstående):
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...');
Och konsolutgång skulle vara detta:
> pass: should return 0...
> pass: should return -1...
> pass: should return 1...
Vi kan nu säkert säga att add(x,y)
... ska returnera summan av två heltal . Vi kan rulla upp dem till något liknande:
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;
}
}
Enhetstestlöften med Mocha, Sinon, Chai och Proxyquire
Här har vi en enkel klass som ska testas som ger ett Promise
baserat på resultaten från en extern ResponseProcessor
som tar tid att köra.
För enkelhets skull antar vi att processResponse
metoden aldrig kommer att misslyckas.
import {processResponse} from '../utils/response_processor';
const ping = () => {
return new Promise((resolve, _reject) => {
const response = processResponse(data);
resolve(response);
});
}
module.exports = ping;
För att testa detta kan vi utnyttja följande verktyg.
Jag använder följande test
skript i min package.json
fil.
"test": "NODE_ENV=test mocha --compilers js:babel-core/register --require ./test/unit/test_helper.js --recursive test/**/*_spec.js"
Detta tillåter mig att använda es6
syntax. Den refererar till en test_helper
som kommer att se ut
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
tillåter oss att injicera vår egen stubbe på platsen för den externa ResponseProcessor
. Vi kan sedan använda sinon
att spionera på den stubbens metoder. Vi använder tillägget för att chai
att chai-as-promised
injicerar för att kontrollera att ping()
-metodens löfte är fullfilled
, och att det eventually
returnerar det nödvändiga svaret.
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);
})
});
Låt oss istället anta att du vill testa något som använder svaret från ping
.
import {ping} from './ping';
const pingWrapper = () => {
ping.then((response) => {
// do something with the response
});
}
module.exports = pingWrapper;
För att testa pingWrapper
utnyttjar vi
Som tidigare tillåter Proxyquire
oss att injicera vår egen stubbe på platsen för det yttre beroendet, i detta fall ping
som vi testade tidigare. Vi kan sedan använda sinon
att spionera på den stubbens metoder och utnyttja sinon-stub-promise
att tillåta oss att returnsPromise
. Detta löfte kan sedan lösas eller avvisas som vi vill i testet, för att testa omslagets svar på det.
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);
});
});