testing in javascript

30
JS Unit Testing For both Backend and Frontend

Upload: digital-natives

Post on 07-May-2015

470 views

Category:

Technology


0 download

DESCRIPTION

Here at Digital Natives we are devoted to support the automated testing of our applications. Lately we write more and more complex business logics on front-end side therefore we need to test front-end side codes more accurately. I put together a presentation for our weekly developer meeting concerning this topic, where I reviewed the current possibilities, but I think that it might be interesting for other front-end programmers too.

TRANSCRIPT

Page 1: Testing in JavaScript

JS Unit TestingFor both Backend and Frontend

Page 2: Testing in JavaScript

Why is unit testing good for js?

● you can develop without a browser● you can test your code automatically● you don’t have to test manually● you don’t have to test via E2E test● you can develop without a browser

○ of course for view development you have to use● you can test you BE code

Page 3: Testing in JavaScript

Prerequisite (conditions)

● well separated code○ like mvc (no spaghetti code)○ small, testable parts (classes)

● zero or minimal dom dependency○ dom is slow ○ very slow and you don’t want to mock it out

Page 4: Testing in JavaScript

How to test JS?

● you need a library○ mocha○ jasmine○ Qunit○ etc

● you have to run your tests○ browser○ node○ karma (odd one out)○ etc

Page 5: Testing in JavaScript

What I’m using

● Mocha○ works with node○ works with browser(s) (karma)

● Chai-TDD○ closest to ruby expect

Page 6: Testing in JavaScript

Example JS code - User Modelvar User;

User = (function() {

function User(plainObject) { this.parse(plainObject); }

User.prototype.parse = function(plainObject) { this.id = plainObject.id; this.first_name = plainObject.first_name; this.last_name = plainObject.last_name; this.status = plainObject.status; };

return User;})();

Page 7: Testing in JavaScript

How can we test Our Model?describe('User/Model', function() { var dummyUserData = { id: 1, first_name: 'First', last_name: 'Last', status: 'locked' };

describe('#initialize', function() { it('should set the provided fields', function() { var user = new User(dummyUserData);

expect(user.first_name).to.eq('First'); expect(user.last_name).to.eq('Last'); expect(user.status).to.eq('locked'); }); });});

Page 8: Testing in JavaScript

Test with a browser

● Create a test.html● Include

○ mocha.js○ chai.js○ user_model.js○ user_model_test.js

● open test.html

Page 9: Testing in JavaScript

open test.html

Page 10: Testing in JavaScript

How to use xhr in your model?User.prototype.get = function(cbSuccess, cbError) { var self = this;

$.get("/users/" + this.id, function(data) { self.parse(data); cbSuccess(); }).fail(function(error, m) { cbError(); }); };

Page 11: Testing in JavaScript

In the browser you should

● include sinon.js○ spy/stub/mock library

● include sinon server○ xhr mocking server

Page 12: Testing in JavaScript

How to test async call? describe('with success xhr', function() { beforeEach(function() {

var response = '{ "id": 1, "first_name": "Second First", "last_name": "Second Last", "status": "active" }';

server = sinon.fakeServer.create(); server.respondWith("GET", "/users/1", [200, { "Content-Type": "application/json" }, response ]);

});

afterEach(function() {

server.restore();

});

it('should set the newly provided data', function(done) { user.get(function() { expect(user.first_name).to.eq('Second First');

.... done(); }); server.respond(); }); });

Page 13: Testing in JavaScript

open test.html

Page 14: Testing in JavaScript

Test with multiple browsers (karma)

● open the browsers(silently)

● run your tests● close the browsers

(optional)● rerun your tests on file

changes (optional)● outputs results to your

console

karma.conf.js

{ frameworks: ['mocha', 'chai'], files: [ 'vendor/**/*.js', 'user_model.js', 'user_model_tests.js' ], autoWatch: false, browsers: ['Chrome', 'Firefox'], singleRun: true};

Page 15: Testing in JavaScript

karma start

Page 16: Testing in JavaScript

Test with node

● basically the same as in browsers○ only difference is the module system require

(‘module’) ● faster than browsers● you can test your BE and FE with the same

test runner ○ if you doesn’t use browser specific stuff (like xhr,

window, dom etc)

Page 17: Testing in JavaScript

Test with nodevar request = require("superagent");var User;

User = (function() { ... User.prototype.get = function(cbSuccess) { var url = "/users/" + this.id;

request.get(url, function(res) { this.parse(res); cbSuccess(); }.bind(this)); }; return User;})();

module.exports = User;

var chai = require('chai');var expect = chai.expect;var sinon = require('sinon');

var User = require('../src/user');var request = require("superagent");

describe('User/Model', function() { beforeEach(function() {

var response = var response = '{ "id": 1, "first_name": "Second First", "last_name": "Second Last", "status": "active" }'; sinon.stub(request, "get").yields(response); });});

....

Page 18: Testing in JavaScript

mocha

Page 19: Testing in JavaScript

● While I develop I want to to test my code via node○ a lot faster○ easier to test partials

● When I build I want to test my code via karma○ we have to know if something is wrong in IE

Same tests with node and karma

Page 20: Testing in JavaScript

Browserify

● you can use nodejs module syntax on the frontend○ var UserModel = require(“Modules/User/Model”);○ var userModel = new UserModel();

● generate one js file with all the dependencies● you can use same libraries on the FE and on

the BE part○ moment.js○ schemata (js validation library)○ your custom lib

Page 21: Testing in JavaScript

Schemate schema def.var schemata = require('schemata');

var UserSchema = schemata({ first_name: { name: 'First Name', type: String, validators: { all: [req] } }, last_name: { name: 'Last Name', type: String, validators: { all: [req] } }, status: { type: String, default: 'locked', validators: { all: [req] } }});

module.exports = UserSchema;

REQUIRE VALIDATOR

var req = function(key, keyDisplayName, object, callback) { var value = object[key]; if (typeof value !== “undefined” && value !== null){ return callback(null, undefined); } else { return callback(null, ' is required' ); }};

FE

buttonClick = function(){

var json = this.toJson();

UserSchema.validate(json, function(errors){ if (Object.keys(errors).length === 0) return sendAjaxToTheServer(json); showErrors(errors); });

}

Page 22: Testing in JavaScript

Use browserify with mocha

● everything is the same as in the node tests

● you have to use karma preprocessor

{ frameworks: ['mocha', 'browserify'], preprocessors: { 'test/*': ['browserify'] }

....};

Page 23: Testing in JavaScript

Test with node / karma var request = require("superagent");var User;

User = (function() { ... User.prototype.get = function(cbSuccess) { var url = "/users/" + this.id;

request.get(url, function(res) { this.parse(res); cbSuccess(); }.bind(this)); }; return User;})();

module.exports = User;

var chai = require('chai');var expect = chai.expect;var sinon = require('sinon');

var User = require('../src/user');var request = require("superagent");

describe('User/Model', function() { beforeEach(function() {

var response = var response = '{ "id": 1, "first_name": "Second First", "last_name": "Second Last", "status": "active" }'; sinon.stub(request, "get").yields(response); });});

....

Page 24: Testing in JavaScript

karma start | mocha

Page 25: Testing in JavaScript

Grunt - Config based Task runner

● There are a lot of contributed tasks○ mochaTest, karma, browserify, concatenate, copy, ftp,

sass, less, etc● you can define complex tasks

○ build (jshint, concatenate, test:unit, test:e2e)○ test (jshint, test:unit)○ deploy (build, ftp)

Page 26: Testing in JavaScript

Grunt exampleYou can create complex tasks

test:

- 'karma' - 'mochaTest'

build: - 'jshint' - 'test' - 'browserify' - 'concatenate' - 'minify' - 'sass' - 'copy'

Simple task, using grunt-contrib-mocha

configuration file:

mochaTest: feTest: options: clearRequireCache: true reporter: 'spec' src: ['test/fe/**/*.js']

beTest: options: clearRequireCache: true reporter: 'dots' src: ['test/be/**/*.js']

Page 27: Testing in JavaScript

grunt test

Page 28: Testing in JavaScript

Grunt watch

● watches for file changes

● runs tasks if one of the specified files have changed

watch: scripts: files: ['test/unit/**/*.js', 'src/js/**/*.js'] tasks: ['jshint', 'mocha'] interrupt: true options: spawn: false jade: files: ['src/view/**/*.jade'] tasks: ['jade', ‘livereload:html’] interrupt: true options: spawn: false stylus: files: ['src/style/**/*.styl'] tasks: ['stylus', ‘livereload:css’] interrupt: false options: spawn: false

Page 29: Testing in JavaScript

grunt watch

Page 30: Testing in JavaScript

https://github.com/Valetudox/js_unit_testing

we are hiring :)