Testes unitários de JS com Jasmine e Karma
-
Upload
douglas-matoso -
Category
Technology
-
view
384 -
download
1
Transcript of Testes unitários de JS com Jasmine e Karma
Testes Unitrios de JS com Jasmine e KarmaDouglas Matoso
Por que fazer testes unitrios no frontend?
D confiana em refatoraes.Identifica bugs mais cedo.Ajuda a documentar o cdigo.Ajuda a melhorar o design. divertido :-DPor qu?
Tools of the trade
Rodar os testes em diferentes navegadores (inclusive headless, como PhantomJS) com apenas um comando.Rodar os testes rapidamente (em segundos) e frequentemente.Rodar os testes em modo watch, onde alteraes no cdigo disparam os testes automaticamente.Poder gerar relatrios diversos (resumo dos testes, cobertura do cdigo testado, etc.)Isolar o frontend do backend, evitando interferncias da rede, do banco de dados, etc.Objetivos
Roda os testes em diferentes navegadores com um comando.Modo watch.Permite gerar relatrios atravs de plugins.Fcil integrao com Gulp, Grunt e pipelines de CI (como Jenkins).Karma: o executador
http://karma-runner.github.io
Karma: config e resultado// karma.conf.js
. . .frameworks: [jasmine],files: [ app/vendor/**/*.js, app/js/**/*.js, test/specs/**/*.js],reporters: [ progress, html, coverage],browsers: [ Chrome, PhantomJS]. . .
Karma e Gulpvar KarmaServer = require('karma').Server, KARMA_CONF_PATH = __dirname + '/karma.conf.js';
gulp.task('test', function (done) { var server = new KarmaServer({ configFile: KARMA_CONF_PATH, browsers: ['Chrome'], singleRun: false }, done); server.start();});Sem Gulp
> karma start
> karma start --single-run
Com Gulp
> gulp test
> gulp tdd
> gulp test-ci
describe(What is being tested, function () { describe(context, function () { beforeEach(function () { ... });
it(should do something, function () { ... });
it(should do something else, function () { ... }); });});Jasmine: o framework
http://jasmine.github.io
it(should sum the numbers, function () { var result = sum(1,2,3); expect(result).toEqual(6);});
it(should return something, function () { var result = foo(); expect(result).not.toBeNull();});Jasmine: Matchers.toEqual(value).toBeNull().toBeDefined().toMatch(regex ou string).toBeTruthy(value) // cast para boolean.toBeFalsy(value) // cast para boolean.toContain(value) // array contm.toBeGreaterThan(value).toBeLessThan(value).toThrow()
// esta sute no vai ser executadaxdescribe(Suite, function () { it(should do something, function () { ... });
it(should do something else, function () { ... });});Jasmine: skip e focusdescribe(Suite, function () { // apenas este teste ser executado fit(should do something, function () { ... });
it(should do something else, function () { ... });});
describe(Product model, function () { beforeEach(function () { this.model = new Product({ id: 123 }); this.server = sinon.fakeServer.create(); this.server.respondWith(GET, /products/123, JSON.stringify({ description: JavaScript Book, price: 19.99 })]); });
afterEach(function () { this.server.restore(); });
it(should fetch product data, function () { this.model.fetch(); this.server.respond(); expect(this.model.get(description)) .toEqual(Javascript Book); });});Sinon: o servidor (fake)http://sinonjs.org
var TestRouter = function () { init: function () { this.server = sinon.fakeServer.create({ respondImmediately: true }); }, enableRoute: function (route) { this.server.respondWith(route.method, route.url, route.response); }})();
------------------------------------------var ProductRoute = { main: { method: GET, url: /products\/(\d+)/, response: JSON.stringify({ description: JavaScript Book, price: 19.99 }) }};Sinon: classe de rotasdescribe(Product model, function () { beforeEach(function () { this.model = new Product({ id: 123 }); TestRouter.init(); TestRouter.enableRoute(ProductRoute.main);
afterEach(function () { TestRouter.restore(); });
it(should fetch product data, function () { this.model.fetch(); expect(this.model.get(description)) .toEqual(Javascript Book); });});
Melhorando a escrita dos testes
// O que mais fcil escrever?// Assim:response: JSON.stringify({ id: 1, description: Product Test, price: 9.99, active: true, stock: 10 })
// Ou assim:response: JSON.stringify(Factory.create(product))
// Mais opesFactory.create(product, { stock: 0 });
Factory.create(inactive-product);
Factory.createList(10, product);Fabricando dados com js-factories// Definio da factoryFactory.define(product, function (attrs) { return _.extend({ id: this.sequence(id), description: Product Test, price: 9.99, active: !this.is(inactive), stock: 10 }, attrs);});https://github.com/matthijsgroen/js-factories
describe(Form validations, function () { beforeEach(function () { this.view = new AppView(); $(body).prepend(this.view.render().el); });
afterEach(function () { this.view.remove(); });Testando a viewit(should validate phone number, function () { var phoneInput = $(#phoneInput), submitBtn = $(#form input:submit), alert;
phoneInput.val(abc); submitBtn.click(); alert = $(.alert-danger);
expect(alert.is(:visible)).toBeTruthy(); expect(alert.text()).toMatch(phone is invalid);});
describe(Form validations, function () { beforeEach(function () { loadFixtures(signup-form.html); });
Custom matchers e HTML fixtures com jasmine-jqueryit(should validate phone number, function () { var phoneInput = $(#phoneInput), submitBtn = $(#form input:submit), alert;
phoneInput.val(abc); submitBtn.click(); alert = $(.alert-danger);
expect(alert).toBeVisible(); expect(alert).toHaveText(phone is invalid);});
https://github.com/velesin/jasmine-jquery// Alguns novos matcherstoBeChecked() toBeDisabled() toBeHidden() toBeVisible() toContainElement(jQuerySelector) toContainHtml(string) toContainText(string) toHandle(eventName) toHaveAttr(attributeName, attributeValue) toHaveClass(className) toHaveCss(css) toHaveText(string) toHaveHtml(string)
describe(Form validations, function () { beforeEach(function () { this.page = new SignupPage(); });
afterEach(function () { this.page.destroy(); });
Testes mais elegantes com Page Objectsit(should validate phone number, function () { var alert;
this.page.setPhoneNumber(abc); this.page.submitForm(); alert = this.page.getAlert();
expect(alert).toBeVisible(); expect(alert).toHaveText(phone is invalid);});
var SignupPage = Class.extend({ init: function () { this.view = new AppView(); $(body).prepend(this.view.render().el); },
destroy: function () { this.view.remove(); },
setPhoneNumber: function (value) { $(#phoneInput).val(value); },
submitForm: function () { $(#form input:submit).click(); },
getAlert: function () { return $(.alert-danger); }});Testes mais elegantes com Page Objects
http://martinfowler.com/bliki/PageObject.html
Show me the code!https://github.com/doug2k1/karma-jasmine-example
THANKYOUFOR YOURTIME!