On Github pascalpp / testing-with-karma-webpack
Our test runner
Our module bundler / compiler
Our testing framework
Testing the individual parts of a feature, separately from each other. This is the focus of this talk.
Testing collections of units working together.Testing whole UI flows, including multiple screens.
main/
client/
src/
index.js
test/
tests.js
tests/
package.json
bower.json
webpack.config.js
karma.conf.js
config/
cd client npm install bower install npm install -g karma-cli webpack
karma start
karma start --single-run
KARMA_BROWSERS=Chrome,Safari karma start
NODE_ENV=production karma start
A few thoughts…
Instead, listen for changes in those objects inside your module
Avoid using AboutMe.on, when, trigger, request, execute
describe('module/foo/foo_view', function() {
// define vars used throughout your suite
var FooView
var foo_view
var node
var region
// don't perform any logic directly in your describes
// use before and after
before(function() {
require('mocks/globals') // sets up a mock globals json glob
FooView = require('module/foo/foo_view')
node = $('<div />').appendTo('body')
region = new Marionette.Region({ el: node })
})
after(function() {
region.destroy()
node.remove()
})
// create a fresh foo_view for each test
beforeEach(function() {
foo_view = new FooView()
region.show(foo_view)
})
// describe each part of your view or object
// write pending tests first
// using it('should do something') with no callback
// implement the test callback later
describe('#ui', function() {
describe('#headline', function() {
it('should exist')
})
describe('#button', function() {
it('should exist')
})
})
describe('#someMethod', function() {
it('should do something')
it('should not do something')
})
})
expect and should
var chai = require('chai')
var expect = chai.expect
chai.should()
describe('#ui', function() {
describe('#headline', function() {
it('should exist', function() {
expect(foo_view.ui.headline).to.exist
})
})
describe('#button', function() {
it('should exist', function() {
foo_view.ui.button.should.exist
})
})
})
Replace a function with sinon.stub
sinon.stub(SomeObject, 'someMethod')
Attach a rider with sinon.spy, allows the original function to run
sinon.spy(SomeObject, 'someMethod')
Don't forget to restore when you’re done
SomeObject.someMethod.restore()
var sinon = require('sinon');
describe('MyObject', function() {
describe('#myMethod', function() {
beforeEach(function() {
sinon.stub(OtherObject, 'otherMethod')
})
afterEach(function() {
Object.otherMethod.restore()
})
it('should call OtherObject.otherMethod', function() {
MyObject.myMethod()
OtherObject.otherMethod.calledOnce.should.be.true
})
})
})
Haven’t used this myself yet, but:
require('jquery.ajaxmock')
$.ajaxMock.register('/n/3/internal/url_exists', {
responseText: '{ exists: true }',
statusCode: 200,
status: 'OK',
type: 'POST', // optional, default: 'GET'
delay: 1000 // optional
})
Write your test in a location similar to the src file being tested
/client/src/module/mymodule/mymodule.js /client/test/tests/module/mymodule.test.js
Require your test in client/test/tests.js
require('./tests/module/mymodule.test.js')
Comment out other tests to see if your test runs independently
Don’t commit with other tests disabled!
require('module/compliment_modal/compliment_modal');
var MyView = Marionette.LayoutView.extend({
someMethod: function() {
AboutMe.trigger('user:compliment:ui');
}
});
var MyView = Marionette.LayoutView.extend({
someMethod: function() {
require.ensure([], function() {
require('module/compliment_modal/compliment_modal');
AboutMe.trigger('user:compliment:ui');
})
}
});
Now we can test our view without loading compliment_modaland all of its dependencies.
globals (required by lib/log, which is required all over the place)
require('mocks/globals')
other dom json (required by lib/analytics, for example)
var insertDomJson = require('mocks/insert_dom_json')
insertDomJson({}, 'json analytics')
Sometimes you just can’t test a unit outside the app context. Too many dependencies to untangle, maybe now isn’t the time to untangle them.
before(function() {
var MockAboutMe = require('mocks/aboutme')
var app = MockAboutMe.setup()
app.start()
})
after(function() {
MockAboutMe.teardown()
})