Recently, I upgraded from ember-qunit 0.3.1 to 0.3.2. The main reason I wanted to upgrade was to take advantage of a new way of writing component tests.
Previously, there was no way to test how the component was actually used. A component test could instantiate a component and poke at its internals, but that isn't how we use components. We use them declaratively in templates. The old style also meant it was difficult to test block templates and impossible to test block parameters.
I really wanted the new test style, but it wasn't easy to get there. The following are my upgrade notes.
Many of my old component tests looked like
moduleForComponent('foo-bar');
That breaks in the latest version because there is no default third argument. Instead, for an old-style unit test, just change the above to
moduleForComponent('foo-bar', 'FooBarComponent', {
integration: false
});
Previously, I had
moduleForModel('user', 'User#hasRole', {
integration: true
});
just so I wouldn't have to declare a long needs
list in the test. (user
has relationships to lots of other models in the system.)
That breaks in ember-qunit 0.3.2, but the workaround is pretty easy:
moduleForModel('user', 'User#hasRole', {
integration: true,
beforeEach: function() {
DS._setupContainer(this.container);
}
});
See emberjs/ember-test-helpers#46 for more information.
In general, I prefer using the Acceptance-test style: visit('/billing');
, click(findButton('Save'))
, etc. I always reach for that test first.
I find it hard to write those tests for general-purpose components. You could test just one instance in your app, but that might not cover the component's whole API. Or you could try to test them all, but you'll get lots of redundancy. I converted most of the tests for general-purpose components into the new integration: true
style.
This new style does away with this.subject(attributes)
in favor of this.render(template)
. Note that the template
is outside the component and must include the use of the component itself. The context outside the component is the test itself. You can call this.set
in your test to set up variables and bindings will work. And this.$()
is a selector scoped to the template rendered by this.render()
.
Overall, I find it to be a good compromise -- easy to get some isolation around the component, yet the tests look more like real-world code and behavior.
One of my tests looks like
moduleForComponent('credit-card', 'CreditCardComponent', {
integration: true,
beforeEach: function() {
this.set('fakeStripe', { createToken: sinon.stub() });
this.set('card', { lastFourDigits: '4416' });
this.render(`
{{credit-card stripe=fakeStripe card=card}}
`);
}
});
test('shows the last four digits', function(assert) {
assert.equal(this.$('.last-four').text(), '4416');
});
test('saving requests a token from Stripe', function(assert) {
Ember.run(() => { this.$('.save').click(); });
assert.ok(this.get('fakeStripe').createToken.calledOnce);
});
moduleForComponent
was fixed in [email protected].