I'm using ember-cli 0.0.35, and injecting a dependency onto my component via an initializer. It works great in development, but the property isn't present when I run tests. It appears that testing calls loadInitializers, but the dependency is not showing up on this.subject({});
I don't want to manually inject it for the tests. Is there a better way to handle this?
Initializer:
var FooServiceInitializer = {
name: 'foo',
initialize: function (container, application) {
application.inject('component:foo', 'foo', 'service:foo');
}
};
export default FooServiceInitializer;
Failing Test:
moduleForComponent('bar', 'Component: Bar', {
setup: function() {
App = startApp();
component = this.subject({});
},
teardown: function () {
Ember.run(App, App.destroy);
}
});
test('Properties: foo', function() {
// Make sure we injected the service
ok(component.foo, 'foo is injected');
});
As I said before, it really lends itself to an integration test since you are testing the container at this point (and not the mini container created by ic-ajax).
Your real test is along the lines of this
test("root lists 3 colors", function(){
var c = App.__container__.lookup('component:foo-bar');
ok(c.foo.blah);
});
If you feel guilty about using the container during testing (which you shouldn't) you can create a helper to avoid having to fix it all over the place when/if an api changes in the future.
Ember.Test.registerHelper('containerLookup',
function(app, look) {
return app.__container__.lookup(look);
}
);
Make sure you define this before
App.injectTestHelpers();
and then your test would look like
test("root lists 3 colors", function(){
var c = containerLookup('component:foo-bar');
ok(c.foo.blah);
});
http://emberjs.jsbin.com/doxigu/edit
Related
I am trying to unit test an Ember addon that has already been written but unfortunately did not get tested at the time of its creation. I would really really like not to have to modify the existing codebase to fit the tests if possible.
Controller Code
foo() {
return this.get('store').query('stuff', query)
.then((data) => {
this.setProperties({
myList: []
});
data.get('content').mapBy('record').forEach((item) => {
// fails here
this.get('myList').pushObject(item);
});
});
});
}
Test Code
beforeEach() {
let data = Ember.Object.create({
content: Ember.A([
{ record: { } },
{ record: { } }
]);
});
this.controller = this.subject({
store: {
query(property, query) {
return new Ember.RSVP.Promise(function (resolve) {
resolve(data);
});
}
}
});
}
test('failing test case', function (assert) {
this.controller.foo();
}
Error:
Uncaught TypeError: _this.get(...).pushObject is not a function
I did some research and found out that Ember extends object prototypes by default so that standard arrays ( [] ) become Ember arrays. I've checked my config/environment.js as mentioned in the how-to-disable prototype extension here and it only returns and empty object {}.
Why doesn't Ember wrap the array and allow me to use pushObject like I would expect in my tests?
PS: I've noticed that there's a package to disable prototype extension aimed at addon authors, but again I do not have this package
https://github.com/rwjblue/ember-disable-prototype-extensions
Using prototype extensions isn't good idea. That's the reason why ember-disable-prototype-extensions was created
No one can give you answer why Ember doesn't extend arrays in you app while you give move information about packages and your environment. But you can fix situation with explicit usage Ember.Array
instead of
this.setProperties({
myList: []
});
use
this.setProperties({
myList: Ember.A()
});
And that will be great cause prevent such errors in future ( on updating dependencies / packages )
I have read and followed EmberJS Service Injection for Unit Tests (Ember QUnit) but I'm still not able to figure where the problem is.
I would like to test if my authentication is working as expected. I have written authenticator for ember-simple-auth and session is injected into route. Code itself is working without any issues.
export default Ember.Route.extend({
authManager: Ember.inject.service('session'),
...
(in actions):
this.get('authManager').invalidate()
Now, I want to create a test which will test if my authentication is working as I expect. So I wish to use authManager directly.
moduleFor('route:index', 'Unit | Route | xyz', {
needs: ['service:session']
});
test('2', function(assert) {
let route = this.subject();
let s = route.get('authManager');
When I print the content of 's', I get ''. If I change this to something else, then response is undefined as can be expected. Problem is when I want to obtain property 'isAuthenticated' or run 'invalidate()'. In these cases I got 'undefined'. What am I doing wrong?
As of Ember 2.13, the correct solution to this is to use this.register:
test('my test', function(assert) {
this.register('service:session', Ember.Service.extend({
/* mock code */
}));
let subject = this.subject();
// test code goes here...
}
In a unit test, we prefer to use mock objects instead of services. In integration tests, we may use real services instead of mocks.
To mock a service, in a unit test:
var stubMyService = Ember.Object.extend({
//This is a mock object, write a code to test component/route!
invalidate: function() {
return 'invalidateCalled';
},
isAuthenticated: function(){
return true;
}
});
To inject this mock object to your component/route use this.subject() creation as following:
test('2', function(assert){
var component = this.subject({
authManager: stubMyService.create()
});
...
});
I have not been able to test React Router with context successfully. I am using
react 0.13.3
react-router 0.13.3
jest 0.3.0
node 0.10.33
and have tried these approaches:
https://labs.chie.do/jest-testing-with-react-router/
https://gist.github.com/alanrubin/da3f740308eb26b20e70
Is there a definitive example?
All links to the "super-secret guide" mentioned in this question (which does not use Jest) are now broken. When I was able to view that guide, it didn't provide any more information than the first link listed above.
Not sure if this is what you're looking for exactly, but I got around this by making a helper function that I use when writing jest tests for components that depend on router state.
//router-test-helper
var Router = require('react-router'),
Route = Router.Route,
TestLocation = require('react-router/lib/locations/TestLocation');
module.exports = function(React){
TestUtils = React.addons.TestUtils;
return {
getRouterComponent: function(targetComponent, mockProps) {
var component,
div = document.createElement('div'),
routes = [
React.createFactory(Route)({
name: '/',
handler: targetComponent
})
];
location = new TestLocation('/');
Router.run(routes, location, function (Handler) {
var mainComponent = React.render(React.createFactory(Handler)(mockProps), div);
component = TestUtils.findRenderedComponentWithType(mainComponent, targetComponent);
});
return component;
}
};
};
I didn't write all of this on my own, most of it I think I pulled from that now defunct guide you linked to. If I remember right... it's been a while.
After you have that you can use this in your tests kinda like this.
//test-example
jest.dontMock('../src/js/someComponent');
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
var routerHelper = require('../router-test-helper')(React);
var SomeComponent = require('../srcs/js/someComponent');
describe('Some Component', function(){
it('should be testable', function(){
var mockProps = {};
var renderedComponent = routerHelper.getRouterComponent(SomeComponent, mockProps);
// Test your component as usual from here.....
///////////////////////////////////////////////
var inputs = TestUtils.scryRenderedDOMComponentsWithTag(renderedComponent, 'input');
//blah blah blah
});
});
This assumes you have React and the helper in your unmocked module paths
If you're actually trying to test things specific to certain routes, or transitioning between routes... I'm not sure if this is a good approach. May be better to use something more intergration test-y, like selenium or something.. Also... this probably won't work once the 1.0 of react router comes out. But it may be even easier to test things thing 'The React Way'(tm) because all the routing stuff will be handled through props. At least that's the impression I get from the the little bit I've read into it.
To anyone who happens to stuck with this problem.
Here's the setup that I've ended up with for my context dependent components (stripped down for simplicity, of course):
// dontmock.config.js contains jest.dontMock('components/Breadcrumbs')
// to avoid issue with hoisting of import operators, which causes
// jest.dontMock() to be ignored
import dontmock from 'dontmock.config.js';
import React from "react";
import { Router, createMemoryHistory } from "react-router";
import TestUtils from "react-addons-test-utils";
import Breadcrumbs from "components/Breadcrumbs";
// Create history object to operate with in non-browser environment
const history = createMemoryHistory("/products/product/12");
// Setup routes configuration.
// JSX would also work, but this way it's more convenient to specify custom
// route properties (excludes, localized labels, etc..).
const routes = [{
path: "/",
component: React.createClass({
render() { return <div>{this.props.children}</div>; }
}),
childRoutes: [{
path: "products",
component: React.createClass({
render() { return <div>{this.props.children}</div>; }
}),
childRoutes: [{
path: "product/:id",
component: React.createClass({
// Render your component with contextual route props or anything else you need
// If you need to test different combinations of properties, then setup a separate route configuration.
render() { return <Breadcrumbs routes={this.props.routes} />; }
}),
childRoutes: []
}]
}]
}];
describe("Breadcrumbs component test suite:", () => {
beforeEach(function() {
// Render the entire route configuration with Breadcrumbs available on a specified route
this.component = TestUtils.renderIntoDocument(<Router routes={routes} history={history} />);
this.componentNode = ReactDOM.findDOMNode(this.component);
this.breadcrumbNode = ReactDOM.findDOMNode(this.component).querySelector(".breadcrumbs");
});
it("should be defined", function() {
expect(this.breadcrumbNode).toBeDefined();
});
/**
* Now test whatever you need to
*/
According to the blog-post for ember-data version 1.0.0-beta.16 the store can now be used as a service:
TweetComposerComponent = Ember.Component.extend({
store: Ember.inject.service()
});
However, I can't figure out how to do qunit unit tests on such a component. I've tried the following:
moduleForComponent('tweet-composer', {
needs: ['service:store']
});
and:
moduleForComponent('tweet-composer', {
needs: ['store:main']
});
And when I do the former I get an error Attempting to register an unknown factory: 'service:store' and if I do the latter then store is undefined.
Thoughts?
(I'm writing a ember-cli style app).
Update:
It seems there's an open issue for this in the ember-test-helpers repo.
While I'm waiting for this fix, I cooked up a helper that can work as a stop-gap measure (coffeescript):
`import TestModuleForComponent from 'ember-test-helpers/test-module-for-component'`
`import { createModule } from 'ember-qunit/qunit-module'`
# This assumes the last argument, the callbacks, is present, although it
# does support the description being an optional argument.
moduleForStoreComponent = ->
args = Array.prototype.slice.call arguments
callbacks = args[args.length-1]
# Wrap the original beforeEach callback in a modified version that
# also sets up the store for the test container.
originalSetup = callbacks.beforeEach
callbacks.beforeEach = ->
DS._setupContainer(#container)
originalSetup.call(#) if originalSetup
callbacks.store = ->
#container.lookup('store:main')
args.unshift TestModuleForComponent
createModule.apply #, args
`export default moduleForStoreComponent`
A unit test is a place where everything works perfectly except the code/component/unit that you are testing.
So, even the store should be assumed to be working perfectly (0 errors/bugs).
Something like this should work in your test:
moduleForComponent('tweet-composer', {
beforeEach: function() {
this.subject({
store: {/*empty object*/}
});
}
});
If parts of your tests depend on data retrieved from the store, you can do something like:
this.subject({
store: {
find: function() {
var mockedModel = Ember.Object.create({/*empty*/});
return mockedModel;
}
}
});
This is to preserve the status of "unit test", if you start including and registering other objects from your app you 'll be actually writing integration tests.
Note:
In general, looking up models directly in a component is an
anti-pattern, and you should prefer to pass in any model you need in
the template that included the component.
http://discuss.emberjs.com/t/should-ember-components-load-data/4218/2?u=givanse
I have a basic QUnit integration test with ember.js 1.3.1
test("try to get html", function() {
App.Bootstrap.init();
visit("/").then(function() {
//do some basic asserts here
});
});
The trick is that my App.Bootstrap.init does some configuration setup (before the app boots).
App.Bootstrap = Ember.Object.create({
init: function() {
App.deferReadiness();
var configuration = ajaxPromise("/imaging_ui/configuration/", "GET");
Ember.run.next(function() {
Ember.RSVP.all([configuration]).then(function(result) {
//do something w/ the result like setup my configuration globally
App.advanceReadiness();
});
});
}
});
The problem is that since "lazy routing" was applied I can't defer /advance myself anymore because I get the error
"You cannot defer readiness since the ready() hook has already been
called"
Here is what my test helper does (basic ember-testing stuff here)
App.setupForTesting();
App.injectTestHelpers();
Note -this works fine in production as the defer / advance work like they would normally (and this init method is invoked inside the following initializer). The below is monkey patched in my test as it runs before I have the chance to mock any xhrs (and because this usually fires an xhr I decided to monkey patch and invoke the init myself before each test run).
App.initializer({
name: 'bootstrap',
initialize: function() {
App.Bootstrap.init();
}
});
After this PR, the visit helper will call App.advanceReadiness(); in the first call.
Make sure that you don't have an advanceReadiness in your test setup, like the following:
module('my module', {
setup: function() {
//...
// App.advanceReadiness(); can be removed
}
});
Otherwise you'll receive that error.