Intro to Testing an
EmberJS Application
Ben Limmer
EmberJS Denver Meetup
7/29/2015
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
slides are available on
slideshare / ember.party
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
why do we test?
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
write better code
less need for
manual qa
confidence when
refactoring
living documentation
confidence when
upgrading
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
3 levels of testing
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
Unit
Integration
Acceptance
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
Unit
Integration
Acceptance
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
Unit
Integration
Acceptance
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
ember unit tests
• actions
• computed properties with sophisticated logic
• mixins
• util functions
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
ember unit tests
function
input
expected
output
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
Unit
Integration
Acceptance
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
ember integration tests
• components
• actions sent from components
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
ember integration tests
component
interaction
expected
state
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
ember integration tests
component
interaction
expected
action
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
Unit
Integration
Acceptance
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
ember acceptance tests
• rendered routes
• transitions between routes
• communicating with the (fake) server
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
ember acceptance tests
application
interaction
expected
state
application
interaction
expected
state
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
qunit vs. mocha
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
qunit vs. mocha (chai)
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
qunit
• 1st class support
• syntax is not as ‘fluent’
qunit
test('it renders', function(assert) {
this.render(hbs`{{foo-bar}}`);
assert.equal(this.$('button').text(), 'Click Me');
});
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
mocha
• Good support, but not 1st class
• Highly fluent syntax
mocha / chai
it('has a button with text', function() {
this.render(hbs`{{foo-bar}}`);
expect(this.$('button').text()).to.equal('Click Me');
});
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
demo
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
don’t panic -
source is on github
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
what do we test?
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
Unit
Integration
Acceptance
mixins/authenticated-route.js
export default Ember.Mixin.create({
session: Ember.inject.service(),
signInRoute: 'sign-in',
beforeModel: function() {
if (this.get('session.isLoggedIn')) {
return;
} else {
this.replaceWith(this.get('signInRoute'));
}
}
});
mixins/authenticated-route-test.js
describe('beforeModel', function() {
const signInRoute = 'sign-in';
let subject, replaceWithStub, session;
beforeEach(function() {
const AuthenticatedRouteObject = Ember.Route.extend(
AuthenticatedRouteMixin);
replaceWithStub = sinon.stub();
session = Ember.Object.create();
subject = AuthenticatedRouteObject.create({
session: session,
signInRoute: signInRoute,
replaceWith: replaceWithStub
});
});
it('does nothing if logged in');
it('transitions to the sign in route if not logged in');
});
it('does nothing if logged in', function() {
session.set(‘isLoggedIn’, true);
subject.beforeModel();
expect(replaceWithStub.called).to.be.false;
});
it('transitions to the sign in route if not logged in',
function() {
session.set('isLoggedIn', false);
subject.beforeModel();
expect(replaceWithStub.withArgs(signInRoute).calledOnce).to.be.true;
});
mixins/authenticated-route-test.js
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
Unit
Integration
Acceptance
components/sign-in-
form.hbs
1 <h4 id='sign-in-greeting'>
2 Click the Button Below to Login as {{user.email}}
3 </h4>
4 <button id='sign-in-cta' class='button' {{action 'signIn'}}>
5 Sign In
6 </button>
components/sign-in-form.js
1 export default Ember.Component.extend({
2 actions: {
3 signIn: function() {
4 this.sendAction(‘signIn', this.get('user'));
5 }
6 }
7 });
components/sign-in-form-
test.js
it('shows the user's email address', function() {
this.set('user', Ember.Object.create({
email: 'foo@bar.com'
}));
this.render(hbs`{{sign-in-form user=user}}`);
expect(this.$('#sign-in-greeting').text()).to.contain('foo@bar.com');
});
it('has a button to login', function() {
this.render(hbs`{{sign-in-form}}`);
expect(this.$('button#sign-in-cta').length).to.equal(1);
});
components/sign-in-form-
test.js
it('sends the login action with the user when clicking the button',
function(done) {
const user = Ember.Object.create({
email: 'foo@bar.com'
});
this.set('user', user);
this.on('signIn', function(actionUser) {
expect(actionUser).to.equal(user);
done();
});
this.render(hbs`{{sign-in-form user=user signIn='signIn'}}`);
this.$('button#sign-in-cta').click();
});
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
Unit
Integration
Acceptance
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
acceptance-test/sign-in-
test.js
describe('success', function() {
beforeEach(function() {
server.create('user', {
email: 'hello@benlimmer.com',
password: 'guest'
});
});
it('transitions to the dashboard on successful login');
it('shows a flash message on successful login');
});
acceptance-test/sign-in-
test.js
it('transitions to the dashboard on successful login', function()
{
visit('/sign-in');
click('button#sign-in-cta');
andThen(function() {
expect(currentPath()).to.equal('dashboard');
});
});
it('shows a flash message on successful login', function() {
visit('/sign-in');
click('button#sign-in-cta');
andThen(function() {
expect(find('.alert-box.success').length).to.equal(1);
});
});
acceptance-test/sign-in-
test.js
describe('failure', function() {
beforeEach(function() {
// API will return 400
server.create('user', {
email: 'notBen@gmail.com',
password: 'guest'
});
});
it('does not transition to the dashboard on failure');
it('shows a danger flash message on login failure');
});
acceptance-test/sign-in-
test.js
it('does not transition to the dashboard on failure', function() {
visit('/sign-in');
click('button#sign-in-cta');
andThen(function() {
expect(currentPath()).to.equal('sign-in');
});
});
it('shows a danger flash message on login failure', function() {
visit('/sign-in');
click('button#sign-in-cta');
andThen(function() {
expect(find('.alert-box.danger').length).to.equal(1);
});
});
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
thank you!
blimmer
l1m5
hello@benlimmer.com
ember.party
Ben LimmerEmberJS Meetup - 7/29/2015 ember.party
demo project code
• https://github.com/blimmer/emberjs-denver-
testing-talk

Automated Testing in EmberJS