unit testing front end javascript
DESCRIPTION
Unit Testing Front End JavaScript with Yuri Takhteyev Presented on September 18 2014 at FITC's Web Unleashed Toronto 2014 Conference More info at www.fitc.ca 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. OBJECTIVE Learn how to write unit tests for front-end code. TARGET AUDIENCE Front-end JavaScript developers not currently using unit testing or those experiencing difficulties with unit testing. ASSUMED AUDIENCE KNOWLEDGE Intermediate front-end JavaScript, no prior experience with front-end unit testing FIVE THINGS AUDIENCE MEMBERS WILL LEARN How unit testing benefits the developer How to structure applications to make them testable How to use Mocha and Karma in unit testing How to mock dependencies (and why) How to handle asynchronous codeTRANSCRIPT
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