What is the best way to test a model's store? I am using ember-data 2.7.0 and would like to test that I can create a model instance and save it to the backend (firebase) successfully.
I have wrapped the var record = store.create and record.save in and Ember.run function but I get You can only unload a record which is not inFlight. `<(subclass of DS.Model):ember227:null>
Lots of way to test this but the one that I prefer is through spying/stubbing using ember-sinon.
Assuming you have this action for creating and saving a record:
import Route from 'ember-route';
export default Route.extend({
actions: {
createAndSaveTheRecord() {
this.store.createRecord('dummy_model', {
id: 'dummy',
name: 'dummy'
}).save();
}
}
});
You can have a test that looks like this:
import sinon from 'sinon';
test('should create a record', function(assert) {
assert.expect(1);
// Arrange
let stub = sinon.stub().returns({save: sinon.stub()});
let route = this.subject({store: {createRecord: stub}});
// Act
route.send('createAndSaveTheRecord');
// Assert
assert.ok(stub.calledWith('dummy_model', {id: 'dummy', name: 'dummy'}));
});
test('should save the created record', function(assert) {
assert.expect(1);
// Arrange
let spy = sinon.spy();
let route = this.subject({
store: {
createRecord: sinon.stub().returns({
save: spy
})
}
});
// Act
route.send('createAndSaveTheRecord');
// Assert
assert.ok(spy.calledOnce);
});
Related
I'm trying to write an acceptance test to see if a certain property in the model for the route I visit equals what I am asserting.
I am not outputting information to the page with this route, instead I will be saving some portion of it to localstorage using an ember addon. So normally I realize I could use a find() to find an element on the page and check it's content to determine if the model is being resolved but that won't work for this case.
In the acceptance test I have this setup (using mirage btw)
test('Returns a user', function(assert) {
// Generate a user
var user = server.create('user',{first_name: 'Jordan'});
// Visit the index page with the users short_url
visit('/' + user.short_url);
var route = this.application.__container__.lookup('route:index');
// Assert that the model the user we created by checking the first name we passed in
assert.equal(route.model.first_name,'Jordan','Model returns user with first name Jordan');
});
But when I run the test it shows the result as being undefined
UPDATE:
After trying Daniel Kmak's answer I still cannot get it to pass. This is the route code I am working with
import Ember from 'ember';
import LocalUser from 'bidr/models/user-local';
export default Ember.Route.extend({
localUser: LocalUser.create(),
navigationService: Ember.inject.service('navigation'),
activate() {
this.get('navigationService').set('navigationMenuItems', []);
},
beforeModel() {
this.localUser.clear();
},
model(params) {
var self = this;
return this.store.queryRecord('user',{short_url: params.short_url}).then(function(result){
if(result){
self.set('localUser.user', {
"id": result.get('id'),
"first_name": result.get('first_name'),
"active_auction": result.get('active_auction'),
"phone": result.get('phone')
});
// transition to event page
self.transitionTo('items');
} else {
self.transitionTo('home');
}
});
}
});
And the test looks like this
import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from 'bidr/tests/helpers/start-app';
module('Acceptance | index route', {
beforeEach: function() {
this.application = startApp();
},
afterEach: function() {
Ember.run(this.application, 'destroy');
}
});
test('Returns a user', function(assert) {
var user = server.create('user',{first_name: 'Jordan'});
visit('/' + user.short_url);
var route = this.application.__container__.lookup('route:index');
andThen(function() {
assert.equal(route.get('currentModel.first_name'),'Jordan','Model returns user with first name Jordan');
});
});
All the code works as it should in development.
Ok, so I've experimented with testing in Ember and it seems you should be good with getting model in andThen hook:
test('returns a user', function(assert) {
visit('/'); // visit your route
var route = this.application.__container__.lookup('route:index'); // find your route where you have model function defined
andThen(function() {
console.log(route.get('currentModel')); // your model value is correct here
assert.equal(currentURL(), '/'); // make sure you've transitioned to correct route
});
});
Taking your code it should run just fine:
test('Returns a user', function(assert) {
var user = server.create('user',{first_name: 'Jordan'});
visit('/' + user.short_url);
var route = this.application.__container__.lookup('route:index');
andThen(function() {
assert.equal(route.get('currentModel.first_name'),'Jordan','Model returns user with first name Jordan');
});
});
Another thing to note is that you can access model via route.currentModel property.
For my model:
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
simple: 'simpleValue',
promise: Ember.RSVP.resolve(5)
});
}
});
In andThen with console.log(route.get('currentModel')); I got:
Object {simple: "simpleValue", promise: 5}
Logged.
How do I access the current model? I am aware of application.__container_.lookup but I understand this is a bit of a hack.
import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from 'myapp/tests/helpers/start-app';
let application;
module('Acceptance | booking/edit', {
beforeEach: function() {
application = startApp();
},
afterEach: function() {
Ember.run(application, 'destroy');
}
});
test('visiting /booking/edit', function(assert) {
visit('/booking/1');
//At this point I would like to access the model returned from the route model hook.
andThen(function() {
assert.equal(currentURL(), '/booking/1');
});
});
Sample Route excerpt.
this.route('booking', { path:'/booking' }, function() {
this.route('edit', { path:'/:booking_id' }, function() {
this.route('account', { path:'/account' });
...
});
...
});
You should be able to use moduleFor and then within the test you can use this.subject() to access the controller.
moduleFor('controller:bookingsEdit', 'Bookings Edit Controller');
If moduleFor is undefined. Then import moduleFor import {moduleFor} from 'ember-qunit';
and then within the test you can use this.subject() to access the controller
moduleFor(fullName [, description [, callbacks]])
fullName: (String) - The full name of the unit, ie
controller:application, route:index.
description: (String) optional - The description of the module
callbacks: (Object) optional - Normal QUnit callbacks (setup and
teardown), with addition to needs, which allows you specify the other
units the tests will need.
http://guides.emberjs.com/v1.10.0/testing/testing-controllers/
https://github.com/rwjblue/ember-qunit
currently I'm changing my project from ember to ember-cli and run in a issue I can't get rid of. I am really not sure where the problem is, since I also updated ember and ember-data.
I get the Error:
Uncaught Error: Cannot re-register: `store:main`, as it has already been resolved.
I'm trying to load a user via the simple-auth Session.
import UserSession from '../session/user';
export default {
name: 'user-session',
before: 'simple-auth',
after: 'store',
initialize: function(container, application) {
container.register('session:user', UserSession);
}
};
and the session:
import Session from 'simple-auth/session';
export default Session.extend({
setup: function(authenticator, content, trigger) {
// Do not let setup trigger
this._super.call(this, authenticator, content, false);
// Lookup user and trigger events ourselves
var store = this.container.lookup('store:main');
console.log(store.find);
var self = this;
store.find('user', content.userId)
.then(function(user) {
self.set('user', user);
self.trigger('sessionAuthenticationSucceeded');
}, function() {
console.log('ERROR: Could not resolve user of session!');
});
}
});
the store.find is there but then the error breaks it.
I also tired to inject the store like this:
Ember-Simple-Auth currentUser example help required but had the same result.
Further I tried to make it via the instance-initalizer for ember-data beta.19.
I do stuff like this:
Session from '../session/user';
export default {
name: 'user-session1',
after: 'ember-data',
initialize: function(container, application) {
var store = container.lookup('store:main');
}
};
but this ends up in:
Uncaught TypeError: container.lookup is not a function
Using:
DEBUG: -------------------------------
ember.debug.js:4874DEBUG: Ember : 1.12.1
ember.debug.js:4874DEBUG: Ember Data : 1.0.0-beta.19.2
ember.debug.js:4874DEBUG: jQuery : 1.11.3
ember.debug.js:4874DEBUG: Ember Simple Auth : 0.8.0
ember.debug.js:4874DEBUG: -------------------------------
Thx for the help
------------------ EDIT --------------------------------------------
I updated my instance-initializers based on #Artych comment where I get the store.
I removed the custom session from simple-auth and tried it with reopening
ENV['simple-auth'] = {
//session: 'session:user', ...
My function:
initialize: function(application) {
var store = application.container.lookup('store:main');
Session.reopen({
setCurrentUser: function() {
console.log('never get here');
var accessToken = this.get('access_token');
var self = this;
if (!Ember.isEmpty(accessToken)) {
//never gets here, doesn't matter if I take other variables
}
}.observes('access_token', 'id', 'userId', 'user_id')
});
}
the problem now is that it never goes into "setCurrentUser". I still can logout and in. my autenticator:
authenticate: function(credentials) {
return new Ember.RSVP.Promise(function(resolve, reject) {
var ttl = 30*60*1000; // Request login for 30 minutes
var data = _.extend(credentials, {ttl: ttl});
Ember.$.ajax({
type: 'POST',
url: ENV.api + '/users/login',
data: data,
dataType: 'json'
})
.then(function(response) {
console.log(response);
Ember.run(null, resolve, response);
}, function(xhr, status, error) {
Ember.run(null, reject, error);
});
});
},
returns:
Object {id: "xI3sPSsgdOiHLd8DcFyuOE42KhbuO8gi8BjWBJRrgHgeCESWoma99C2RtvC6tnxG", ttl: 1800000, created: "2015-07-02T14:00:06.600Z", userId: 1}
As you can see, I added a bunch of observed variables: observes('access_token', 'id', 'userId', 'user_id') which I saw in different other questions but nothings helps.
Any idea on this?
Thx
I Solved it.
Simple don't use Ember Data 1.0.0-beta.19.2. I upgraded to 1.13.4 and I was able to get the store like before.
I'm building an Ember CLI app (v0.2.3), and I have some unit tests that have been generated for me for the adapters and serializer in my app. The generated code looks like this:
// app/serializers/my-model-test.js
// Replace this with your real tests.
test('it serializes records', function (assert) {
var record = this.subject();
var serializedRecord = record.serialize();
assert.ok(serializedRecord);
});
and
// app/adapter/application-test.js
// Replace this with your real tests.
test('it exists', function (assert) {
var adapter = this.subject();
assert.ok(adapter);
});
What do I put in these tests? I've built acceptance tests and unit tests for my models and components, but not sure what needs to go in these unit tests. Haven't been able to find documentation on building these unit tests, nor can I find any example applications on GH that have built these tests out.
If you want to create unit tests for your adapters and serializers, you should look at how ember data tests those themself. Basically, you can look at the test for the RESTSerializer etc. and use their technique.
Example serializer: https://github.com/emberjs/data/tree/master/tests/integration/serializers
The code that ember data uses to achieve this: https://github.com/emberjs/data/blob/master/tests/helpers/store.js
I found it much easier to write an integration test for my custom serializer. I tried Steffans suggestion but I couldn't get it load anything other than the base JSONSerializer. The code I wrote that is working in Ember 1.13.8, Ember Data 1.13.15 is below.
import { moduleFor, test } from 'ember-qunit';
moduleFor('application', 'Integration | Serializer | application', {
integration: true
});
test('Serializer normalizes correctly for basic single object', function(assert) {
assert.expect(1);
let store = this.container.lookup('service:store');
var basicPeterJson = {
id: 1,
title: 'Mr',
firstName: 'Peter',
lastName: 'Parker'
};
var expectedHash = {
data: {
type: 'contact',
id: 1,
attributes: {
title: 'Mr',
firstName: 'Peter',
lastName: 'Parker'
},
relationships: {}
}
};
var contact = store.serializerFor('application').normalizeResponse(store, store.modelFor('contact'), basicPeterJson, 1);
assert.deepEqual(contact, expectedHash);
});
I placed this in tests/integration/serializers/my-serializer-test.js
Testing the adapter:
test('it has a url for creating a record', function (assert) {
const url = this.subject().urlForCreateRecord('person', { firstName: 'Bob' });
assert.equal(url, 'https://example.com/path/to/api');
});
Testing the serializer:
test('it serializes records', function (assert) {
const serialized = this.subject({
foo: 'bar',
}).serialize();
assert.equal(serialized.foo, 'bar');
});
For testing other serializer functions, I previously followed #Knightsy's integration test example and it worked for me. Many thanks! Then, I worked out that this can actually be simplified and unit tested (if you can call it that).
My test goes like this:
moduleForModel('person', 'Unit | Serializer | person', {
needs: ['serializer:person'],
});
test('testing', function (assert) {
const serializer = this.container.lookup('service:store').serializerFor('person');
const payload = {
id: 3,
};
const response = serializer.normalizeSingleResponse(null, null, payload, payload.id);
assert.equal(response.data.id, 3);
});
I can't find a single example in google for unit test of views in Ember CLI that renders the view (without renders all app).
I wanna this for test events registered inside of didInserElement hook.
For components i can find docs very easy. For render the component in a test with moduleForComponent just do:
test("component test", function(){
var component = this.subject(),
element = this.append();
ok(element.is('.clickable'), 'has the clickable class');
});
But how i do this for views?
I use this way to render only the view in unit tests:
One important thing to note, is that you need to needs all templates, partials and helpers explicitly. Otherwise the test will fail due to lookup errors.
tests/unit/views/main-test.js:
import Ember from 'ember';
import { test, moduleFor } from 'ember-qunit';
var view;
moduleFor('view:main', 'MainView', {
needs: ['template:main'], // won't find the 'main' template without this
setup: function() {
var controller = Ember.ObjectController.extend({
//mockController if needs
}).create();
view = this.subject({
controller: controller,
templateName: 'main',
});
Ember.run(function() {
view.appendTo('#ember-testing');
});
},
teardown: function() {
Ember.run(function() {
view.destroy();
});
},
});
test("didInsertElement", function(){
var element = Ember.$('.main');
var controller = view.get('controller');
var eventForPressCtrlAltM = Ember.$.Event( "keydown", { which: 77, altKey: true, ctrlKey: true } );
Ember.run(function() {
element.trigger(eventForPressCtrlAltM);
});
strictEqual(/* ... */);
});