Szukaj…
Podstawowe twierdzenie
Na najbardziej podstawowym poziomie testowanie jednostkowe w dowolnym języku zapewnia stwierdzenia dotyczące niektórych znanych lub oczekiwanych wyników.
function assert( outcome, description ) {
var passFail = outcome ? 'pass' : 'fail';
console.log(passFail, ': ', description);
return outcome;
};
Popularna powyższa metoda asercji pokazuje nam jeden szybki i łatwy sposób na potwierdzenie wartości w większości przeglądarek internetowych i interpretatorów, takich jak Node.js, z praktycznie każdą wersją ECMAScript.
Dobry test jednostkowy ma na celu przetestowanie dyskretnej jednostki kodu; zwykle funkcja.
function add(num1, num2) {
return num1 + num2;
}
var result = add(5, 20);
assert( result == 24, 'add(5, 20) should return 25...');
W powyższym przykładzie zwracana wartość z funkcji add(x, y)
lub 5 + 20
to oczywiście 25
, więc nasze potwierdzenie 24
powinno zakończyć się niepowodzeniem, a metoda assert zarejestruje wiersz „fail”.
Jeśli po prostu zmodyfikujemy oczekiwany wynik asercji, test się powiedzie, a wynikowy wynik będzie wyglądał mniej więcej tak.
assert( result == 25, 'add(5, 20) should return 25...');
console output:
> pass: should return 25...
To proste stwierdzenie może zapewnić, że w wielu różnych przypadkach funkcja „dodaj” zawsze zwróci oczekiwany wynik i nie wymaga żadnych dodatkowych ram ani bibliotek do działania.
Bardziej rygorystyczny zestaw asercji wyglądałby tak (używając var result = add(x,y)
dla każdego asercji):
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...');
Wyjście konsoli byłoby następujące:
> pass: should return 0...
> pass: should return -1...
> pass: should return 1...
Możemy teraz bezpiecznie powiedzieć, że add(x,y)
… powinno zwrócić sumę dwóch liczb całkowitych . Możemy je zwinąć w coś takiego:
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;
}
}
Testy jednostkowe obiecują Mocha, Sinon, Chai i Proxyquire
Tutaj mamy do przetestowania prostą klasę, która zwraca Promise
na podstawie wyników zewnętrznego ResponseProcessor
którego wykonanie zajmuje dużo czasu.
Dla uproszczenia założymy, że metoda processResponse
nigdy nie zawiedzie.
import {processResponse} from '../utils/response_processor';
const ping = () => {
return new Promise((resolve, _reject) => {
const response = processResponse(data);
resolve(response);
});
}
module.exports = ping;
Aby to przetestować, możemy wykorzystać następujące narzędzia.
Używam następującego skryptu test
w moim pliku package.json
.
"test": "NODE_ENV=test mocha --compilers js:babel-core/register --require ./test/unit/test_helper.js --recursive test/**/*_spec.js"
To pozwala mi używać składni es6
. test_helper
do test_helper
który będzie wyglądał
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
pozwala nam wstrzyknąć własny Proxyquire
w miejsce zewnętrznego Proxyquire
ResponseProcessor
. Następnie możemy użyć sinon
do szpiegowania metod tego kodu pośredniczącego. Używamy rozszerzeń do chai
które chai-as-promised
wstrzykuje, aby sprawdzić, czy obietnica metody ping()
jest fullfilled
i czy w eventually
zwraca wymaganą odpowiedź.
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);
})
});
Zamiast tego załóżmy, że chcesz przetestować coś, co korzysta z odpowiedzi ping
.
import {ping} from './ping';
const pingWrapper = () => {
ping.then((response) => {
// do something with the response
});
}
module.exports = pingWrapper;
Aby przetestować pingWrapper
wykorzystujemy
Tak jak poprzednio, Proxyquire
pozwala nam wstrzyknąć własny Proxyquire
w miejsce zależności zewnętrznej, w tym przypadku testowaną wcześniej metodą ping
. Następnie możemy użyć sinon
do szpiegowania metod tego returnsPromise
i wykorzystać sinon-stub-promise
aby umożliwić nam returnsPromise
. Obietnicę tę można następnie rozwiązać lub odrzucić, jak chcemy w teście, w celu przetestowania odpowiedzi opakowania na to.
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);
});
});