Download - Testing in JavaScript
JS Unit TestingFor both Backend and Frontend
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
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
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
What I’m using
● Mocha○ works with node○ works with browser(s) (karma)
● Chai-TDD○ closest to ruby expect
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;})();
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'); }); });});
Test with a browser
● Create a test.html● Include
○ mocha.js○ chai.js○ user_model.js○ user_model_test.js
● open test.html
open test.html
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(); }); };
In the browser you should
● include sinon.js○ spy/stub/mock library
● include sinon server○ xhr mocking server
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(); }); });
open test.html
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};
karma start
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)
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); });});
....
mocha
● 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
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
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); });
}
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'] }
....};
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); });});
....
karma start | mocha
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)
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']
grunt test
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
grunt watch
https://github.com/Valetudox/js_unit_testing
we are hiring :)