Ember Addon: writing unit tests for files in my addon folder - unit-testing

I am writing an Ember Addon that provides some services that are not exposed through the app/ folder.
// project Foo
// addon/services/foo.js
import Ember from 'ember';
export default Ember.Service.extend({})
The unit test that gets generated by Ember CLI uses moduleFor helper.
// tests/unit/services/foo.js
import { moduleFor, test } from 'ember-qunit';
moduleFor('service:foo', 'Unit | Service | foo', {
// Specify the other units that are required for this test.
// needs: ['service:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
let service = this.subject();
assert.ok(service);
});
Problem is that since my FooService is not exposed through the app/ folder, moduleFor helper cannot find it using service:foo name.
What would be the best way to unit test my service here?
I can see three possibilities:
1) add tests/dummy/app/services/foo.js that exports FooService
// tests/dummy/app/services/foo.js
export { default } from 'foo/services/foo.js';
2) create initializers in the dummy app that registers service:foo
// tests/dummy/app/initializers/account-data.js
import FooService from 'foo/services/foo'
export function initialize(application) {
application.register('service:foo', FooService);
}
EDIT
turns out I can't do this. it's not that moduleFor helper is trying to find 'service:foo' but trying to register 'app/services/foo' as 'service:foo'. So registering 'service:foo' ahead of the time does not help.
3) don't use moduleFor helper.
EDIT
By this I meant something like what #Andy Pye's answer. But it would be nice to be able to use moduleFor helper... Especially for models since I need access to store.
EDIT
turns out it gets more complicated for models since I can't create them directly. So if I go with not using moduleFor helper, I need an instead of store object...

I've just had the same (or a very similar) issue. I've found that this seems to work:
// tests/unit/services/foo.js
import { module, test } from 'ember-qunit';
import FooService from 'Foo/services/foo';
module('Unit | Service | foo', {
// Specify the other units that are required for this test.
// needs: ['service:foo']
});
// Replace this with your real tests.
test('it exists', function (assert) {
let service = FooService.create();
assert.ok(service);
});

Related

Integration test rendering process - EmberJS

I'm writing an integration test for one of my addon components. I've also linked it to a sibling project using npm link:
.. projects/
.... my-project/
.... my-linked-project/
I use one of the my-linked-project mixins in the my-project component in question.
//my-project/addon/components/my-component/component.js
import Ember from 'ember';
import ExternalMixin from 'my-linked-project/mixins/foo'
export default Ember.Component.extend(ExternalMixin, {
...
}
This runs fine with the application running, but seems to hit some issues when I render it in my integration test.
//my-project/tests/integration/components/my-component-test.js
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('my-component', 'Integration description', {
integration: true
}
test('should render', function() {
this.render(hbs`{{my-component}}`);
}
However I get Error: Could not find module 'my-linked-project/mixins/foo'.
Where can I read more about what happens when render / hbs are called?
Could the issue be the location of the test file, which can't find the external linked project?
It's worth removing the npm link from tech equation temporarily to see if that's causing your problems. What you have there looks fine ...

Unit test for helpers Ember-cli

When I create a new helper on Ember CLI, a test is created inside tests/unit/helpers. I would like to know how to test this helpers using the unit test?. I tried to find a documentation, but nothing helps. I need to test the function createArray in order to pass the test coverage to a 100%.
Now, this is my helper:
import Ember from 'ember';
export function createArray(array) {
return array;
}
export default Ember.Helper.helper(createArray);
This is my unit test: tests/unit/helpers/create-array-test.js
module('Unit | Helper | create array');
// Replace this with your real tests.
test('it works', function(assert) {
let result = createArray([42]);
assert.ok(result);
});
Hope someone can guide me.
What version of Ember CLI are you using? The blueprint should generate a test file with an import of the named export so you can use it directly.
What export function createArray does is create a named export named createArray. This means you can import the function directly and use it like you a normal function:
import { createArray } from 'app-name/helpers/create-array'
createArray(arrayOfArguments);
I modified #ykaragol's Twiddle to demonstrate how to do this in a test:
import { createArray } from 'app-name/helpers/create-array';
import { module, test } from 'qunit';
module('Unit | Helper | create array');
test('it works', function(assert) {
let result = createArray([42]);
assert.ok(result);
});
Replace app-name with the name of your application.
The code posted on twiddle helped me understand how to solve the helpers unit test. This is how I tested it, and it works like a charm.
test('Return the argument is passed', function(assert) {
assert.ok(createArray([8,4,5,6]));
assert.ok(createArray(['test1','test2','test3']));
});

How to unit test custom Ember.Inflector rules

I'm trying to create a test for the following initializer:
import Ember from 'ember';
const { Inflector } = Ember;
export function initialize() {
Inflector.inflector.uncountable('settings');
Inflector.inflector.uncountable('feedback');
}
export default {
name: 'inflector',
initialize
};
Ember-cli generates the following test for an initializer:
import Ember from 'ember';
import InflectorInitializer from 'redirect/initializers/inflector';
import { module, test } from 'qunit';
let application;
module('Unit | Initializer | inflector', {
beforeEach() {
Ember.run(function() {
application = Ember.Application.create();
application.deferReadiness();
});
}
});
test('it works', function(assert) {
InflectorInitializer.initialize(application);
assert.ok(true);
});
As I understand it, a beforeEach() hook creates an application first, and then pauses it so the initializer test can run.
The test fails because Ember.Inflector in undefined when the initializer is called by the test.
How do I test custom inflector rules?
I'd like to have the following assertions:
assert.equal(inflector.singularize('settings'), 'settings');
assert.equal(inflector.pluralize('settings'), 'settings');
assert.equal(inflector.singularize('feedback'), 'feedback');
assert.equal(inflector.pluralize('feedback'), 'feedback');
assert.equal(inflector.rules.uncountable['settings'], true);
assert.equal(inflector.rules.uncountable['feedback'], true);
I'd be willing to configure inflector rules in a different way if this will make it testable.
I tried to create an application and remove deferReadiness to then see If I can put those assertions in the test. But even though Ember.Inflector is then defined, it doesn't contain my custom rules.

Ember.js: How to integration test components that interact with ember-data models

I'm building a relatively straight-foward comment-list component. I want to pass in the commentable model (say a Post) and have the component take care of creating, editing, deleting comments. Right now I pass around all the various actions and it's been extremely brittle.
How do I create a true instance of an Ember Data model in a component integration test?
My immediate thought was to import the model then .create({}) it but that errors with use this.store.createRecord() instead
/* jshint expr:true */
import { assert } from 'chai';
import { describeComponent, it } from 'ember-mocha';
import hbs from 'htmlbars-inline-precompile';
import Post from 'ownersup-client/post/model';
describeComponent( 'comment-list', 'Integration: CommentListComponent', {
integration: true
},
function() {
it('renders all of the comments', function() {
const model = Post.create({ title: 'title' });
model.get('comments').createRecord({ body: 'One Comment' })
this.render(hbs`{{comment-list model=model}}`);
assert.lengthOf(this.$('.comment-list-item'), 1);
});
}
);
Anyone have any thoughts?
Among all Ember test helpers, the store is only available from moduleForModel.
Here's how this test helper does it (source):
var container = this.container;
var store = container.lookup('service:store') || container.lookup('store:main');
You can do the same inside your test. You can also put it into a helper so that you don't have to copy-paste a lot.
Note that it will only work for an integration test. You can turn any test into integration one by starting the app using the startApp test helper that is bundled with your Ember CLI boilerplate.
The new ember mocha release 0.8.4 contains a new, public way to inject services such as the store. There's a guides section coming soon with an example (see https://github.com/emberjs/guides/blob/master/source/testing/testing-components.md)
essentially, in your beforeEach you want add the following line: this.inject.service('store');, making the store accessible as this.get('store') in your tests.
Here's a link to the pull request for the new change: https://github.com/switchfly/ember-test-helpers/pull/105
General answer for people who are struggled with similar things related to injecting in integration tests.
Everything depends on which version and solutions you have in your project.
When you have an integration test with module
(import { module } from 'ember-qunit';)
you can use this.owner.lookup('service:store') inside your test
for more information, there is a great article from Dockyard
https://dockyard.com/blog/2018/01/11/modern-ember-testing

installing an Ember helper through add on then using it in a component breaks the tests

I'm installing the 'ember-moment' helper and then I'm using it in components. As soon as I do that, those components' tests break saying pretty much nothing:
not ok 120 PhantomJS 1.9 - StatusCardComponent: it renders
---
actual: >
null
message: >
Died on test #2 at http://localhost:7357/assets/test-support.js:2779
at test (http://localhost:7357/assets/test-support.js:1796)
at http://localhost:7357/assets/client.js:11190
at http://localhost:7357/assets/vendor.js:150
at tryFinally (http://localhost:7357/assets/vendor.js:30)
at http://localhost:7357/assets/vendor.js:156
at http://localhost:7357/assets/test-loader.js:29
at http://localhost:7357/assets/test-loader.js:21
at http://localhost:7357/assets/test-loader.js:40
at http://localhost:7357/assets/test-support.js:5545: Assertion Failed: A helper named 'moment' could not be found
Log: >
...
And here is the code of the test itself, it's just the auto generation:
import {
moduleForComponent,
test
} from 'ember-qunit';
moduleForComponent('event-card', {
// Specify the other units that are required for this test
// needs: ['component:foo', 'helper:bar']
});
test('it renders', function(assert) {
assert.expect(2);
// Creates the component instance
var component = this.subject();
assert.equal(component._state, 'preRender');
// Renders the component to the page
this.render();
assert.equal(component._state, 'inDOM');
});
I tried a bunch of ways of adding this helper to the test as a dependency like needs: ['helper:moment'] and adding its initializer signature to the moduleForComponent function's params, but nothing worked and I can't find any information about it. Help is much appreciated.
Edit: According to this PR, you should be able to specify { integration: true } to turn your existing unit test into an integration test, which eliminates the need for needs:.
moduleForComponent('post', { integration: true });
More background can be found here: https://github.com/rwjblue/ember-qunit/issues/108. Essentially, the integration flag disables the isolated container for the test, so that it has access to the app's resolver.
Note: Requires ember-qunit#0.2.11.
Old Answer: Take a look at this PR: https://github.com/switchfly/ember-test-helpers/pull/21 which adds moduleForIntegration. Here's the relavant bit:
This adds a new kind of test module that fills a gap in the current
set of possible tests. So far we have:
• unit tests that are good for testing algorithmic correctness in isolation.
• acceptance tests that are good for testing within a complete
application.
But we're missing the ability to integration test a unit smaller than
whole-application. This PR adds a new TestModuleForIntegration that
lets you drive an integration test with a template. I think this is
often a more appropriate way to test Ember Components than a unit
test, because interesting components tend to have rich dependence on
other components.
You can see it in use in liquid-fire tests:
/* global sinon */
import Ember from "ember";
import moduleForIntegration from "../../helpers/module-for-integration";
import { test } from "ember-qunit";
moduleForIntegration('Integration: liquid-if');
test('it should render', function(assert) {
this.set('person', 'Tom');
this.render(`
{{#liquid-if isReady}}
{{person}} is ready
{{else}}
{{person}} is not ready
{{/liquid-if}}
`); // }}`)
assert.equal(this.$().text().trim(), 'Tom is not ready');
this.set('person', 'Yehuda');
assert.equal(this.$().text().trim(), 'Yehuda is not ready');
this.set('isReady', true);
assert.equal(this.$().text().trim(), 'Yehuda is ready');
});
So this is what ended up working:
import {
moduleForComponent,
test
} from 'ember-qunit';
import Ember from 'ember';
import { initialize } from '../../../../../initializers/ember-moment';
moduleForComponent('event-card', {
// Specify the other units that are required for this test
// needs: ['helper:moment'],
setup: function (container) {
Ember.run(function () {
// these two arguments are not used
// but probably still good to pass them in
// in the event we leverage them in the future
initialize(container);
});
}
});
test('it renders', function (assert) {
assert.expect(2);
// Creates the component instance
var component = this.subject();
assert.equal(component._state, 'preRender');
// Renders the component to the page
this.render();
assert.equal(component._state, 'inDOM');
});
Do you see that line that is commented out that reads:
// needs: ['component:foo', 'helper:bar']
You need to uncomment it out and make it read:
needs: ['helper:moment']
That should help, if your moment helper is registered correctly