Sinonjs test method that consumes a promise - javascript

I have a javascript method that looks something like this:
someMethod=function(){
somePromise.run().then(()=>{
//do success stuff
}).catch((err)=>{
//do fail stuff
registerError(err);
});
}
I want to make sure that the error is getting registered, so I've set up the following test.
it('should register error', ()=>{
somePromise = {
run: sinon.stub
};
registerError = sinon.stub;
somePromise.returns(Promise.reject({err: 'foo'}));
someMethod();
assert(registerError.calledWith({err: 'foo'}));
});
This test fails because the assert fires before the promises in someMethod finish. I could update someMethod to return a promise, but that smells fishy to me. Any input is welcome, thanks!

You have created an async method that does not return either a promise or contain a callback as an argument. This is not a good practice as you cannot know when your async function will finish. A simple fix is to return the promise from the function and perform your assert as a part of the promise chain.

Related

Unit testing NodeJS Promise inside a function

Im trying to unit test a function that calls a promise...
Using Mocha, Sinon. I have a functional block like this:
myfile.js:
let OuterDependecy = require('mydep');
function TestFunction(callback) {
OuterDependency.PromiseFunction().then(response => {
//some logic here
}).catch(err => {callback(err)});
inside my test i have used proxyquire to mock the outerdependecy
testfile.js
let proxyquire = require('proxyquire');
let OuterDepStub = {};
let testingFunc = proxyquire('myfile.js', {'mydep': OuterDepStub});
... then inside my testing block
let stubCallback = function() {
console.log('Stub dubadub dub'); //note...i can use sinon.spy here instead
};
beforeEach(()=>{
OuterDependency.PromiseFunction = function(arg) {
return new Promise((resolve, reject)=>{
reject('BAD');
});
};
spy = sinon.spy(stubCallback);
});
my actual test now calls the main "testfunction"
it('Catches Errors, and calls back using error', done => {
TestFunction(stubCallback);
expect(spy).to.have.been.called;
done();
});
I see the stub being called (the console log, hence why i didnt want to use sinon.spy) but the spy is saying its not called. and unit test fails.
I believe this is probably due to a race condition of sorts where the promise is resolving after my test is run... is there anyway to delay the test until my promise is resolve.
i know in in angularjs promise testing, there was a way to "tick" the promise so it resolves when you want to. possible in nodejs?
is there anyway to delay the test until my promise is resolve.
As far as I understand your issue, yes, you should only call done() after the promise is settled. In order to do that,you need two things:
1- Enforce TestFunction to return a Promise, so you can wait until it resolves:
function TestFunction(callback) {
return OuterDependency.PromiseFunction().then(response => {
//some logic here
}).catch(err => { callback(err) });
}
2- Wait to that promise to settle, then call done.
it('Catches Errors, and calls back using error', done => {
TestFunction(stubCallback).then(() => {
expect(spy).to.have.been.called;
done();
})
});
now, our then block won't run until the catch block within TestFunction, so if the test works as expected (i.e. the catch block fires and the callback gets fired), the expectation and the done calls will always fire after the callback gets called.
I see the stub being called (the console log, hence why i didnt want to use sinon.spy) but the spy is saying its not called. and unit test fails.
That's because your expectation runs right after the TestFunction calls, without waiting for it to settle. However, it will get called lately, thus the console.log appears in the next spec.

How to test a method returning a Promise

I'm writing an Angular 2 RC5 application, and unit testing using Karma and Jasmine.
I have a method that returns a Promise<Foo> (It's on top of a call to angular's http.post) I want to run some assertions after that finishes.
Something like this doesn't work
let result = myService.getFoo();
result.then(rslt => expect(1+1).toBe(3)); // the error is lost
This creates an 'Unhandled Promise rejection' warning, but the error is suppressed and the test passes. How do I run assertions based on my resolved promise?
Notes:
The .catch() method doesn't seem to be what I'm after. I don't want to log or do anything that continues normal program flow, I want to fail the test.
I've seen code that looks like $rootScope.$digest();. I'm not sure what the typescript equivalent of this sort of thing is. There doesn't seem to be a way of saying: "I have a promise, I'm going to wait here until I have a synchronous result".
The test should look something like this:
it('should getFoo', function (done) {
let result = myService.getFoo();
result
.then(rslt => expect(rslt).toBe('foo'))
.then(done);
});
Using the done callback works, but you should be able to do this as well:
(Note the return)
it('should getFoo', function () {
let result = myService.getFoo();
return result
.then(rslt => expect(rslt).toBe('foo'))
});

Executing callback from a CucumberJS with Selenium step definition

I'm trying out CucumberJS with Selenium and PhantomJS. I've successfully built a World object using this StackOverflow answer as a guide.
So now I'm testing out some basic step definitions, but having some confusion about how to execute the callback at the end of the step. This works great:
module.exports = function () {
this.World = require("../support/world.js").World;
this.Given(/^I am visiting Google$/, function (callback) {
this.driver.get('http://www.google.com')
.then(function() {
callback();
});
});
};
The driver hits Google.com and the callback isn't fired until after the requested document is loaded. But I find this syntax to be a little wordy, so I thought maybe I could just pass callback straight to the then() after my first promise, like so:
module.exports = function () {
this.World = require("../support/world.js").World;
this.Given(/^I am visiting Google$/, function (callback) {
this.driver.get('http://www.google.com')
.then(callback);
});
};
This, however fails, and seems to console.log the callback. Here's the output:
Scenario: Googling # features/theGoogle.feature:6
Given I am visiting Google # features/theGoogle.feature:7
[object Object]
(::) failed steps (::)
[object Object]
What's going on here? I was expecting that callback could simply be passed to the then() function and executed after the promise is fulfilled. Why would wrapping it in an anonymous function make it work?
What is happening is that callback is being called with the arguments of driver.get().then.
In other words, here is what happens:
this.driver.get('http://www.google.com')
.then(function(result) {
callback(result);
});
The problem is that cucumber's callback will consider a fail if the callback is called with something as its first parameter, since it is supposed to be an error as in callback(new Error('Something went wrong')).
To me this was enough to ban the use of callbacks completely. Selenium is fully oriented promise, and you should keep with promise only if you want your life to be easier. This is perfect since cucumber.js accepts promises to be returned instead of a callback, so here is the best way to do things:
// Be sure to omit the last parameter of the function, usually "callback"
this.Given(/^I am visiting Google$/, function () {
return this.driver.get('http://www.google.com');
});
The step will fail if the promise is eventually rejected, or keep on to the next step if the promise is fullfiled. But in both cases, cucumber will wait for the last promise, so all you need to do is always return the very last promise of any step, since Selenium will only resolve/reject the last promise after the previous ones have been resolved. Everything looks much nicer doesn't it?

How to correctly chain promises in complex resource loading sequence?

In angular code, I have a chained promise like this:
// a function in LoaderService module.
var ensureTypesLoaded= function(){
return loadContainerTypes($scope).then(loadSampleTypes($scope)).then(loadProjectList($scope)).then(loadSubjectTypes($scope));
}
Each of these functions return a promise, that loads things from a resource and additionally modifies $scope on error and success, e.g.:
var loadProjectList = function ($scope) {
// getAll calls inside a resource method and returns promise.
return ProjectService.getAll().then(
function (items) {
// succesfull load
console.log("Promise 1 resolved");
$scope.projectList = items;
}, function () {
// Error happened
CommonService.setStatus($scope, 'Error!');
});
};
I intent to use in in a code in a controller initialziation as such:
// Part of page's controller initialization code
LoaderService.ensureTypesLoaded($scope).then(function () {
// do something to scope when successes
console.log("I will do something here after all promises resolve");
}, function () {
// do something when error
});
However, this does not work as I'd like to. Ideally message "I will do something here after all promises resolve" must appear after all promises resolve. Instead, I can see that it appears earlier than messages from resolved promises within functions that are listed ensureTypesLoaded.
I would like to create a function ensureTypesLoaded such, that:
it returns a promise that is resolved when all chained loads are resolved;
if any of "internal" promises fail, the function should not proceed to next call, but return rejected promise.
obviously, if I call ensureTypesLoaded().then(...), the things in then() must be called after everything inside ensureTypesLoaded is resolved.
Please help me with correct building of chained promises.
I thinks that problem is in your loadProjectList function. Because .then() should receive function which is called at resultion time. Usually is function returning chain promise.
But in your case you call all load immediately in parallel. It's little complicated with $scope passing. But I think your code should appear like this
//this fn is called immediatly on chain creation
var loadProjectList = function ($scope) {
//this fn is called when previous promise resolves
return function(data) {
//don't add error handling here
ProjectService.getAll().then(...)
}
}
This cause serial loading as you probably want. (just notice: for parallel excution in correct way $q.all is used)
Finally you should have error handler only in ensureTypesLoaded, not in each promise.

How to test promises with Mocha

I'm using Mocha to test an asynchronous function that returns a promise.
What's the best way to test that the promise resolves to the correct value?
Mocha has built-in Promise support as of version 1.18.0 (March 2014). You can return a promise from a test case, and Mocha will wait for it:
it('does something asynchronous', function() { // note: no `done` argument
return getSomePromise().then(function(value) {
expect(value).to.equal('foo');
});
});
Don't forget the return keyword on the second line. If you accidentally omit it, Mocha will assume your test is synchronous, and it won't wait for the .then function, so your test will always pass even when the assertion fails.
If this gets too repetitive, you may want to use the chai-as-promised library, which gives you an eventually property to test promises more easily:
it('does something asynchronous', function() {
return expect(getSomePromise()).to.eventually.equal('foo');
});
it('fails asynchronously', function() {
return expect(getAnotherPromise()).to.be.rejectedWith(Error, /some message/);
});
Again, don't forget the return keyword!
Then 'returns' a promise which can be used to handle the error. Most libraries support a method called done which will make sure any un-handled errors are thrown.
it('does something asynchronous', function (done) {
getSomePromise()
.then(function (value) {
value.should.equal('foo')
})
.done(() => done(), done);
});
You can also use something like mocha-as-promised (there are similar libraries for other test frameworks). If you're running server side:
npm install mocha-as-promised
Then at the start of your script:
require("mocha-as-promised")();
If you're running client side:
<script src="mocha-as-promised.js"></script>
Then inside your tests you can just return the promise:
it('does something asynchronous', function () {
return getSomePromise()
.then(function (value) {
value.should.equal('foo')
});
});
Or in coffee-script (as per your original example)
it 'does something asynchronous', () ->
getSomePromise().then (value) =>
value.should.equal 'foo'

Categories