Jasmine vs. Mocha, Chai і Sinon

Тестування в JS ставати все більш поширеною практикою. Але з чого почати? Існує безліч фреймворків які надають API для написання JS тестів.

Дана стаття — це короткий огляд відмінностей між двома популярними фреймворками для тестування JS: Jasmine 2 і Mocha. Ми також обговоримо найбільш полулярные бібліотеки Chai і Sinon які часто використовуються в зв'язці з Jasmine і Mocha.

1. API (application programming interface)
API в Jasmine і Mocha дуже схожі. Вони обидва підтримують написання тестів використовуючи BDD (Behavior Driven Development) підхід. Ви можете запитати: «що таке BDD»? Якщо коротко, це підхід до написання тестів, який надає можливість опису функціональності на розмовному мовою.

describe('calculator', function() {
describe('add()', function() {
it('should add 2 numbers togoether', function() {
// assertions here
});
});
});


Затвердження (assertions), або очікування(expectations), як їх часто називають, різняться подані фреймворках. Mocha не має вбудованої assertion бібліотеки. Існує декілька варіантів для використання в середовищі Node.js і для браузерів: Chai, should.js, expect.js, and better-assert. Більшість розробників выберают Chai як assertion бібліотеки. Так як ні одного з assertion бібліотек немає в постачанні з Mocha, вам потрібно буде підключити її у вашу тестову середу. У Chai сужествует три типу assertions:

1) should (повинен)
2) expect (очікувати)
3) assert (стверджувати)

Тип expect аналогічний expect, який надає нам фреймворк Jasmine. Наприклад, якщо ви хочете написати перевірку методу add і ваше очікування що calculator.add(1, 4) буде дорівнювати 5, то дана перевірка буде виглядати аналогічно використовуючи як Jasmine так і Chai.

//Jasmine
expect(calculator.add(1, 4)).toEqual(5);


//Chai
expect(calculator.add(1, 4)).to.equal(5);

Дуже схоже, вірно? Якщо ви переходите з Jasmine на Mocha то шлях досить простий — використовувати Chai з типом expect.

2. Test Doubles (Дублери)
Test Doubles замінюють один об'єкт на інший для тестових цілей. В Jasmine роль test doubles виконують spies(шпигуни). Spy — це функція, яка заменет оригінальну функцію, логіку якої ми хочемо змінити і описує дана функція буде використовуватися в рамках виконання тесту.

Spies дозволяють:

1) Дізнатися кількість раз що викликалася spy функція
2) Вказати значення, що повертається, для того, щоб досліджуваний код продовжив працювати за потрібною алгоритмом.
3) Вказати, щоб spy функція кинула помилку.
4) Дізнатися з якими аргументами була викликана spy функція.
5) Вказати, щоб spy функція викликала оригінальну функцію. (Яку вона замінила)

В Jasmine spy для існуючого методу можливо так:

var userSaveSpy = spyOn(User.prototype, 'save');

Також можливо створити spy, навіть якщо у вас немає методу, який ви хочете замінити.

var spy = jasmine.createSpy();

Mocha не має вбудованої test doubles бібліотеки з цього вам потрібно завантажити і підключити Sinon у вашу тестову середу. Sinon дуже потужна Test Doubles бібліотека яка надає еквівалентний функціонал spies в Jasmine і трохи більше. Варто відзначити, що Sinon розбиває test doubles на три різні категорії: spies, stubs mocks, між якими є тонкі відмінності.

Spy функція Simon викликається за допомогою оригінального методу, в той час як в Jasmine це поведінка треба вказувати. Наприклад:

spyOn(user, 'isValid').andCallThrough() // Jasmine
// is equivalent to
sinon.spy(user, 'isValid') // Sinon

В даному прикладі, буде викликаний оригінальний user.isValid.

Наступний тип test doubles stubs, який замінює оригінальний метод. Поведінка stubs аналогічно з поведінкою за замовчуванням spies в Jasmine, в якому оригінальний метод не викликається.

sinon.stub(user, 'isValid').returns(true) // Sinon
// is equivalent to
spyOn(user, 'isValid').andReturns(true) // Jasmine

В даному прикладі, якщо буде викликаний метод user.isValid, оригінальний метод user.isValid викликаний не буде, а буде викликана його підроблена версія яка повинна повернути результат «true».

З свого досвіду, spies в Jasmine покривають майже все що требуеться для test doubles, тому в більшості випадків вам не потрібно буде Sinon якщо ви використовуєте Jasmine, однак, якщо ви хочете, у вас є можливість використовувати їх спільно. Основна причина, по якій я використовую Sinon разом з Jasmine, це йогоfake server.

3. Асинхронні тести (Asynchronous Tests)
Асинхронне тестування в Jasmine 2.Mocha x і реалізується аналогічно.

it('should resolve with the User object', function(done) {
var dfd = new $.Deferred();
var promise = dfd.promise();
var stub = sinon.stub(User.prototype, 'fetch').returns(promise);

dfd.resolve({ name: 'David' });

User.get().then(function(user) {
expect(user instanceof User).toBe(true);
done();
});
});

В даному прикладі User — функція конструктор у якої є статичний метод get. Метод get всередині себе використовує метод fetch який виконує XHR запит (request). Я хочу перевірити, що коли метод get отримає значення, то це значення буде екземпляр (instance) User. Так як я використав «stub» для методу User.prototype.fetch і вказав йому повернути заздалегідь визначений promise, реальний XHR запит не виконується. Покриття даного коду продовжує бути асинхронним.

Дуже просто вказати, що callback функція в it конструкції очікує аргумент (в даному випадки done) і test runner буде чекати поки виконається функція до того, як закінчить тест. Тест буде припинений і виводиться помилка, якщо аргумент не буде викликаний на протязі певного часу. Це дає повний контроль над тим, коли тести закінчать виконання. Тест, написаний вище, буде працювати як в Jasmine так і в Mocha.

Якщо ви працюєте з Jasmine 1.3 асинхронне тестування виглядає не так «чисто».

Приклад асинхронного тестування в Jasmine 1.3:

it('should resolve with the User object', function() {
var flag = false;
var david;

runs(function() {
var dfd = new $.Deferred();
var promise = dfd.promise();

dfd.resolve({ name: 'David' });
spyOn(User.prototype, 'fetch').andReturn(promise);

User.get().then(function(user) {
flag = true;
david = user;
});
});

waitsFor(function() {
return flag;
}, 'should get resolve with the model', 500);

runs(function() {
expect(david instanceof User).toBe(true);
});
});

В даному прикладі, для завершення асинхронної операції тест буде чекати максимум 500 мілісекунд, в іншому випадку тест буде провалений. Функція waitsFor() постійно перевіряє flag, як тільки flag стане true виконання продовжиться і буде викликаний наступний runs блок.

4. Sinon Fake Server
Одна особливість, яку має Sinon в порівнянні з Jasmine це fake server (підроблений сервер). Це дозволяє встановити підроблені відповіді на певні AJAX запити.

it('should return a collection object containing all users', function(done) {
var server = sinon.fakeServer.create();
server.respondWith("GET", "/users", [
200,
{ "Content-Type": "application/json" },
'[{ "id": 1, "name": "Gwen" }, { "id": 2, "name": "John" }]'
]);

Users.all().done(function(collection) {
expect(collection.toJSON()).to.eql([
{ id: 1, name: "Gwen" },
{ id: 2, name: "John" }
]);

done();
});

server.respond();
server.restore();
});

В даному прикладі пославши GET запит на /users, ми отримаємо відповідь 200, що містить дані про двох користувачів — Гвене і Джона. Це може бути дуже зручно з кількох причин. По-перше, ви можете тестувати код який робить AJAX запити незалежно від того, яку AJAX бібліотеку ви використовуєте. По-друге, ви можете протестувати функцію яка робить AJAX запит і робить попередню обробку повернутих даних перед тим, як виконати обіцянку (resolve promise). По-третє, є можливість оголосити кілька відповідей на успішний запит. Наприклад, у випадках: успішного зняття коштів з кредитної картки, застарілої карти, неправильного CVV коду і т. д. прийдуть різні відповіді. Якщо ви працювали з AngularJs, то Sinon Fake Server схожий на $httpBackend сервіс.

Підсумок:
Jasmine фреймворк включає в себе майже все що необхідно, включаючи assertions, test doubles (реалізований через spies) функціональність. Mocha не володіє такою функціональністю, але надає вибір бібліотеки для assertions (найпопулярніша Chai). Для test doubles Mocha також вимагає підключення додаткової бібліотеки, в більшості випадків це sinon.js. Sinon також може бути відмінним доповненням, надаючи свій fake server (підроблений сервер).

Вибрати тестовий фреймворк для JS може бути важким завданням, сподіваюся, що ця стаття допомогла вам зробити правильний вибір. У будь-якому разі, що б ви ні використовували, ви не помилитеся. Вдалого тестування!

Статья-оригинал
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.