Ember.js Testing async action in controller - ember.js

I've a controller with an action that invoke a method that do some async stuff and return a promise.
export default Ember.Controller.extend({
_upload: function() {
// return a promise
},
actions: {
save: function(item) {
this._upload(item).then(function(response) {
// Handle success
}, function(error) {
// Handle error
}
}
}
});
I would like to unit test the code under Handle success and Handle error.
In my unit test I've mocked the _uploadMethod using
controller.set("_upload", function() {
return new Ember.RSVP.Promise(function(resolve) {
resolve({name: "image1"});
});
});
And then I invoke the action and assert that the success handler has done is job
controller.send("save", "item");
assert.equal(controller.get("selected.item"), "item");
The problem is that the assertion fails because it's run before the promise is resolved and all the stuff in success handler is completed.
How can I wait the promise to resolve before the assertion is checked?

What if you try this instead:
controller.set("_upload", function() {
const promise = new Ember.RSVP.Promise(function(resolve) {
resolve({name: "image1"});
});
promise.then(() => Ember.run.next(() => {
assert.equal(controller.get("selected.item"), "item");
}));
return promise;
});
controller.send("save", "item");
A bit hacky way, but it might work.

To test async methods, you can use the test helper waitUntil to wait for the expected return of the method, like the code below.
controller.send('changeStepAsyncActionExample');
await waitUntil(() => {
return 'what you expect to the Promise resolve';
}, { timeout: 4000, timeoutMessage: 'Your timeout message' });
// If not timeout, the helper below will be executed
assert.ok(true, 'The promise was executed correctly');

Related

How to write a unit test with data fetching, that alters data based on respone in vuejs?

I am trying to write a unit test for a function that does an async call, but it doesnt seem to alter the data prop, maybe I am doing something wrong.
Check the code below:
getSomething() {
MyService.getThis().then(
response => {
this.status = true;
}
).catch(error => {})
}
TestCase:
describe('test', () => {
beforeEach(() => {
// To ignore the created hook, but this doesnt work, any idea?
spyOn(CustomerData, 'created');
spyOn(MyService, 'getThis').and.returnValue(Promise.resolve(list));
});
wrapper = shallowMount(MyComponent, {
propsData: {
data: {}
},
});
it('should work', () => {
wrapper.vm.getSomething();
expect(wrapper.vm.status).toBeTruthy();
});
});
}
The status should be true, but it is false, but if I print the value of status in the getSomething() function it is indeed true. I have no idea what the issue can be.
update:
In the test case I wrote
it('should work', async () => {
await wrapper.vm.getSomething();
expect(wrapper.vm.status).toBeTruthy();
}
and this seems to work. Is this a good way to solve it? Would love to hear other solutions.
Also I am very interested if it is possible to ignore the created hook, I havent been able to figure that out yet.
Code that running inside getSomething() is asynchronous. MyService.getThis() returns promise, and its execution takes time, in case if you fetching some data from remote serivce.
So first of all you need to return promise from getSomething()
getSomething() {
return MyService.getThis()
.then(response => { this.status = true; })
.catch(error => {})
}
And inside the test you need to return promise outside, to let jest know that your test is asynchronous.
it('should work', () => {
return wrapper.vm.getSomething().then(() => {
expect(wrapper.vm.status).toBeTruthy();
});
});
Or as you mentioned in the edited part you can use async version:
it('should work', async () => {
await getSomething();
expect(wrapper.vm.status).toBeTruthy();
});

Async validation never returns in loopback

I have an async validation in one of my models in which I query for a related object to validate it's existence. The problem is that the request is timing out on this validation and the server never responds.
module.exports = function(Ip) {
// Required fields
Ip.validatesPresenceOf('server_id');
...
Ip.validateAsync('server_id', isExistingServer, {
message: 'invalid server'
});
function isExistingServer(err, done) {
var ServerModel = Ip.app.models.Server;
var self = this;
process.nextTick(function() {
ServerModel.findById(self.server_id, function(e, server) {
console.log(_.isNull(server));// this actually prints false
return _.isNull(server) ? err() : done();
});
});
}
};
Because you need execute done(); then err();
According to documentation you should call done() after err. This is the example in docs:
User.validateAsync('name', customValidator, {message: 'Bad name'});
function customValidator(err, done) {
process.nextTick(function () {
if (this.name === 'bad') err();
done();
});
});
Note that as there is no return before the call to err(), done will also be called.

Backbone tests returning timeout

I am getting a timeout of 2000ms exceeded message when running the following tests for my Backbone application.
How can I get this test to pass?
I am also trying to listen for the event's being triggered when calling more. How could this be tested?
describe("Foo.Collection.Items/Discover", function () {
describe("More method", function () {
beforeEach(function () {
this.server = sinon.fakeServer.create();
this.hasLength = sinon.spy(Foo.View.ItemFeed.prototype, "toggleFeedback");
this.noLength = sinon.spy(Foo.View.ItemFeed.prototype, "stopLazyload");
this.item1 = new Foo.Model.item({
description: "A swell minions movie!",
for_sale: false,
title: "Minions: Goldfinger",
link: "/discover",
'private': false,
'main_image': false
});
this.item2 = new Foo.Model.item({
description: "A round pot",
for_sale: true,
title: "Pot",
link: "/discover",
'private': true,
'main_image': true
});
this.itemsDiscover = new Foo.Collection.Items([this.item1, this.item2], {url: "/discover"});
});
afterEach(function () {
this.server.restore();
this.hasLength.restore();
this.noLength.restore();
});
it("should fetch items and trigger moreFetched", function (done) {
this.server.respondWith('GET', "/discover", [
200,
{"Content-type": "application/json"},
JSON.stringify([this.item1, this.item2])
]);
this.itemsDiscover.once("add", function () {
expect(this.itemsDiscover).to.have.length(2);
expect(this.hasLength).to.be.calledOnce();
done();
});
this.itemsDiscover.more();
});
});
});
The part of the Backbone Collection I am trying to test:
more: function () {
var collection = this;
this.fetch({
success: function (collection, response) {
if (response.length) {
collection.trigger('moreFetched');
} else {
collection.trigger('emptyFetched');
}
},
reset: false,
remove: false
});
}
I guess that you have an exception that avoids to call done, try
this.itemsDiscover.once("add", function () {
try {
expect(this.itemsDiscover).to.have.length(2);
expect(this.hasLength).to.be.calledOnce();
done();
} catch(e) {
done(e);
}
});
If you get an error then post it, because I think what it is.
I think I see what's going on: your test method is dependent on the add event being triggered in order to get into the callback that verifies the data. However, the way you've set up this.itemsDiscover, it contains this.item1 and this.item2 already:
....
this.itemsDiscover = new Foo.Collection.Items([this.item1, this.item2], {url: "/discover"});
Then, your mocking out of the URL will also return the string version of an array containing this.item1 and this.item2:
...
this.server.respondWith('GET', "/discover", [
200,
{"Content-type": "application/json"},
JSON.stringify([this.item1, this.item2])
]);
...
Here's the kicker: the add event will never be fired by the collection, because the two items returned already exist in the collection. D'oh.
I suggest changing the test to wait for a sync event instead, which is always fired after a successful fetch:
this.itemsDiscover.once("sync", function () {
expect(this.itemsDiscover).to.have.length(2);
expect(this.hasLength).to.be.calledOnce();
done();
});
I expect that will get your test code running.

Why isn't this rejected RSVP promise not calling catch()?

In my Ember RSVP code, I'm not able to get any error handlers called, even though I see that my 'reject' function is executing successfully. Code:
var promise = new Promise(function(resolve, reject) {
doStuff().then(function() {
resolve(1);
}, function() {
console.log('rejected!');
reject('This has been rejected!');
});
});
var allPromises = [promise]; // Real code has more than one; just one for demonstration.
Ember.RSVP.all(allPromises)
.then(
function() {
console.log('I am called!');
},
function() { console.log('I am never called!');})
.catch(function(err) {
console.log('I am never called, either.');
});
However, I do see the 'rejected!' message in the console. What have I done wrong here? Shouldn't the catch() fire since the reject() worked properly?
DEBUG: Ember : 1.9.0-beta.1
DEBUG: Ember Data : 1.0.0-beta.12
DEBUG: jQuery : 1.9.1
Docs: https://github.com/tildeio/rsvp.js
They state 'Sometimes you might want to work with many promises at once. If you pass an array of promises to the all() method it will return a new promise that will be fulfilled when all of the promises in the array have been fulfilled; or rejected immediately if any promise in the array is rejected.'
You are catching the error in your onReject handler. If you want your catch to run you will have to either not supply a onReject handler or throw an error in the reject handler:
var promise = new Promise(function(resolve, reject) {
reject("")
});
var allPromises = [promise];
// Alt 1 - Dont supply a onRejected handler
Ember.RSVP.all(allPromises)
.then(function() {
console.log('I am called!');
}).catch(function(err) {
console.log('I am never called, either.');
});
// Alt 2 - Throw error in onRejected handler
Ember.RSVP.all(allPromises)
.then(function() {
console.log('I am called!');
}, function () {
console.log('I am never called!');
throw new Error("boom");
}).catch(function(err) {
console.log('I am never called, either.');
});
Alt 1 will print:
I am never called, either.
Alt 2 will print:
I am never called!
I am never called, either.

where to delete database and close connection after all tests using mocha

I'm trying to figure out where to put a function to delete database and close connection after all tests have run.
Here are my nested tests:
//db.connection.db.dropDatabase();
//db.connection.close();
describe('User', function(){
beforeEach(function(done){
});
after(function(done){
});
describe('#save()', function(){
beforeEach(function(done){
});
it('should have username property', function(done){
user.save(function(err, user){
done();
});
});
// now try a negative test
it('should not save if username is not present', function(done){
user.save(function(err, user){
done();
});
});
});
describe('#find()', function(){
beforeEach(function(done){
user.save(function(err, user){
done();
});
});
it('should find user by email', function(done){
User.findOne({email: fakeUser.email}, function(err, user){
done();
});
});
it('should find user by username', function(done){
User.findOne({username: fakeUser.username}, function(err, user){
done();
});
});
});
});
Nothing seems to work. I get Error: timeout of 2000ms exceeded
You can define a "root" after() hook before the 1st describe() to handle cleanup:
after(function (done) {
db.connection.db.dropDatabase(function () {
db.connection.close(function () {
done();
});
});
});
describe('User', ...);
Though, the error you're getting may be from the 3 asynchronous hooks that aren't informing Mocha to continue. These need to either call done() or skip the argument so they can be treated as synchronous:
describe('User', function(){
beforeEach(function(done){ // arg = asynchronous
done();
});
after(function(done){
done()
});
describe('#save()', function(){
beforeEach(function(){ // no arg = synchronous
});
// ...
});
});
From the docs:
By adding a callback (usually named done) to it() Mocha will know that it should wait for completion.
I implemented it a bit different.
I removed all documents in the "before" hook - found it a lot faster than dropDatabase().
I used Promise.all() to make sure all documents were removed before exiting the hook.
beforeEach(function (done) {
function clearDB() {
var promises = [
Model1.remove().exec(),
Model2.remove().exec(),
Model3.remove().exec()
];
Promise.all(promises)
.then(function () {
done();
})
}
if (mongoose.connection.readyState === 0) {
mongoose.connect(config.dbUrl, function (err) {
if (err) {
throw err;
}
return clearDB();
});
} else {
return clearDB();
}
});