unit testing front-end javascript
DESCRIPTION
Building complex software application can be made much easier with unit testing – a fact well established in back-end work but often overlooked on the front-end. Yuri Takhteyev will look at some of the tools and techniques for unit testing front end code, focusing on Mocha and Karma. Most of the examples will draw on AngularJS but the main ideas are applicable to other frameworks as well.TRANSCRIPT
UNIT TESTING FRONT END JAVASCRIPT
YURI TAKHTEYEV
@QARAMAZOV@RANGLEIO
Why Bother with Unit Tests?
Why Bother with Unit Tests?
TDD Lite
Writing Testable Code
☛ Modular code
☛ AngularJS
services
☛ Other modules not entangled with DOM
Keeping Tests Simple
Common Tools
Runner: KarmaTask Automation: Gulp || GruntScorer: Mocha || JasmineAssertions: Chai || JasmineSpies: Sinon || Jasmin
+ CI tools (e.g. Magnum-CI)
http://yto.io/xunit
Installing the Example Code
# First installgit clone https://github.com/yuri/webu-unit.gitcd webu-unitnpm installsudo npm install -g gulpsudo npm install -g bowerbower install
# Now rungulp karma
☛ You’ll need to install git and node before
A Basic Testfunction isOdd(value) { return (value % 2 === 1);}
describe('isEven', function () { it('should handle positive ints', function () { if (isOdd(2)) { throw new Error('2 should be even'); } });});
☛ Let’s put this in “client/app/is-odd.test.js”
Chaidescribe('isEven', function () { it('should handle positive ints', function () { expect(isOdd(1)).to.be.true; expect(isOdd(2)).to.be.false; expect(isOdd(3)).to.be.true; });});
☛ More Chai at http://chaijs.com/api/bdd/
Extending Testsdescribe('isEven', function () { ... it('should handle negative ints', function () { expect(isOdd(-1)).to.be.true; });});
Extending Testsdescribe('isEven', function () { ... xit('should handle negative ints', function () { expect(isOdd(-1)).to.be.true; });});
Extending Testsdescribe('isEven', function () { ... it.only('should handle negative ints', function () { expect(isOdd(-1)).to.be.true; });});
Extending Testsdescribe('isEven', function () { ... it('should handle negative ints', function () { expect(isOdd(-1)).to.be.true; });});
function isOdd(value) { return (value % 2 === 1);}
Testing a Serviceangular.module('app.tasks', [ 'app.server'])
.factory('tasks', function(server) { var service = {};
service.getTasks = function () { return server.get('tasks'); };
return service;});
☛ Let’s put this in “client/app/tasks-service.js”
The Test, Take 1describe('tasks service', function () { beforeEach(module('app.tasks')); it('should get tasks', function() { var tasks = getService('tasks'); expect(tasks.getTasks()).to .not.be.undefined; });});
☛ Let’s put this in “client/app/tasks-service.test.js”
Error: [$injector:unpr] Unknown provider:serverProvider <- server <- tasks
☛ See “client/testing/test-utils.js” for implementation of getService().
Mocking Dependenciesvar data;beforeEach(module(function($provide){ $provide.service('server', function() { return { get: function() { return Q.when(data); } }; }); $provide.service('$q', function() { return Q; });}));
Chrome 37.0.2062 (Mac OS X 10.9.4): Executed 3 of 3 SUCCESS (0.046 secs / 0.027 secs)
Let’s Extend the Serviceservice.getMyTasks = function () { return server.getTasks() .then(function(taskArray) { return _.filter(taskArray, function(task) { return task.owner === user.userName; }); });};
☛ We’ll need to inject “user” into the service
Mocking the User$provide.service('user', function() { return { username: 'yuri' };});
☛ The mock can be very simple
An Async Test, Wrongit('should get user\'s tasks', function() { var tasks = getService('tasks'); data = [{ owner: 'bob', description: 'Mow the lawn' }, { owner: 'yuri', description: 'Save the world' }]; tasks.getMyTasks() .then(function(myTasks) { expect(myTasks.length).to.equal(1); });});
☛ Always check that “wrong” tests fail!
An Async Test, Rightit('should get user\'s tasks', function() { var tasks = getService('tasks'); data = [{ owner: 'bob', description: 'Mow the lawn' }, { owner: 'yuri', description: 'Save the world' }]; return tasks.getMyTasks() .then(function(myTasks) { expect(myTasks.length).to.equal(1); });});
Spies with Sinon$provide.service('server', function() { return { get: sinon.spy(function() { return Q.when(data); }) };});
var server = getService('server');return tasks.getMyTasks() .then(function(myTasks) { expect(myTasks.length).to.equal(1); server.get.should.have.been.calledOnce; });
Thank You.Contact: [email protected] http://yto.io @qaramazov
This presentation: http://yto.io/xunit
by dunechaser
Image Credits
by lincolnblues
by spenceyc
by creative_tools
by mycroyance
by angeljimenez
by snre