How to handle nested asynchronous operation in unit tests - javascript

I have a Javascript module, which is accessing Promise object from another module and then transforming that for it's own use. I am using Bluebird library which ensures, that all promise handlers are called asynchronously. That's quite problem for the testing, especially when that internal promise is not exposed.
module.exports = (testedModule, app) ->
app.module('otherModule').somePromise.then transformValue
transformValue = (val) ->
return new Extra.TransformedValue(val)
In the tests, I am mocking that first promise, so I have access to that. Second promise stays inside the module and I don't want to expose it just for the sake of the tests. Note that I am using Mocha+Chai+Sinon.
beforeEach ->
#initModule = -> app.module('testedModule', testedModule) # prepare function to initialize tested module
#dfd = dfd = Promise.defer() # defer promise resolution to tests
app.module 'otherModule', (otherModule) ->
otherModule.somePromise = dfd.promise
#transformSpy = sinon.spy Extra, 'TransformedValue' # spy on the constructor function
#promiseTransform = dfd.promise.then =>
# this usually fails as the spy is called more then once due to async nature of the tests
#transformSpy.should.have.been.calledOnce
# promise gets resolved with the return value of the spy
# thus it should contain created instance of the TransformedValue
return #transformSpy.firstCall.returnValue
afterEach ->
#transformSpy.restore()
Some preparations for each test. Simply there is promiseTransform, that gets resolved using dfd.resolve() in each test separately. However the transformSpy itself is attached to global object which is shared by all tests (maybe that should be stubbed too). Most of the tests looks like this:
it 'should test whatever...', ->
#init() # initialize module
# something else is tested here, doesn't matter
# note that #dfd is not resolved here, thus transformValue is not called yet
That works just fine, but then comes the test that actually resolves dfd and everything gets messy here. Sometimes spy is resolved more than once or not at all. It's very confusing race of async operations.
it 'should instantiate TransformedValue with correct argument', (done) ->
expected = {foo: "bar"}
#promiseTransform.then (transformed) =>
# test here that TransformedValue constructor has been called once
# ... it actually FAILS, because it was called twice already!
#transformSpy.withArgs(expected).should.have.been.calledOnce
transformed.should.be.an.instanceof Extra.TransformedValue
# somehow this resolves promise for previous test and
# it causes `transformValue` to be called back there
#dfd.resolve expected
#init()
I have spent like 2 days on this and it's driving me nuts already. Tests should be a tool and the actual code to create. There is probably some obvious solution I am missing out.
Do you have any general (or concrete) tips how to handle this with less confusion and more control and determinism? Currently I am thinking about stubbing whole Promise to actually make it synchronous. But it seems to me, that it sort of invalidates the tests, because the workflow can be different than in real run.

What's with the spies? You wouldn't use spies if this was synchronous code. If everything was synchronous, how would you write the test?
Why not write the test as:
it('should instantiate TransformedValue with correct argument', function() {
var expected = {};
return transform(expected).then(function(val) {
assert.deepEqual(Extra.TransformedValue.value, expected)
assert(val instanceof Extra.TransformedValue);
});
});

Related

Flux + Sinon + Promises test always passes or times out

I am currently attempting to test Flux with Sinon + Sinon-As-Promised, but I am struggling to find relevant examples, and having an issue with my test always returning true, no matter the assertion.
My test currently looks like this:
it('Form store should contain encounter object', function() {
var stub = sinon.stub(restService, "loadForm").resolves(mockDataEncounter.Encounter);
stub().then(function(value){
console.log('Inside frmSrv');
formStore._currentForm = value;
expect(formStore._currentForm).to.have.property('Name');
})
console.log('calling action creator');
actionCreator.loadForm("123456789012345678910003");
})
What I was expecting to happen was -
Call action creator -> Action creator usually makes the API call,
but in this case calls the stubbed method instead -> Promise is
resolved, _currentForm is set to the mock data, and then the assertion
is checked.
However what actually happens is:
Call action creator -> Test passes -> Promise resolves
If I add a done callback to the test, and then call done after the assertion, this causes things to execute in my expected order, but then if I modify the expected property to "Name1"(which doesnt exist), the test fails with a timeout error rather than the correct error to say the property doesnt exist.
Am I missing something fundamental here, or just going about things completely the wrong way?
So after some more fiddling I worked it out, I had to add a catch to the promise, and then call done passing in the error in order to get the correct failure message to display.
it('Form store should contain encounter object', function(done) {
var stub = sinon.stub(restService, "loadForm").resolves(mockDataEncounter.Encounter);
stub().then(function(value){
console.log('Inside frmSrv');
formStore._currentForm = value;
expect(formStore._currentForm).to.have.property('Name');
done();
}).catch(function(err){
done(err);
});
console.log('calling action creator');
actionCreator.loadForm("123456789012345678910003");
})

How do I use Mocha/Chai to start checking for DOM elements after a JavaScript Object create it?

Would I need to use a setTimeout? (And it works when I do). The problem I'm having is that the FeedObject does an asynchronous call, so when a new instance of it is created, it takes sometime before it can inject itself to the DOM.
describe('Default Case', function () {
before(function () {
$divHolder = $('#divHolder');
$divHolder.html('');
var myComponent = new FeedObject({
div: $divHolder[0],
prop: {
id: 'myFeed',
},
client: {}
});
myComponent.createDOMElements();
});
it('should exist', function () {
console.log($divHolder.find('.feed-item')); // This does not exist unless I use a timeout
});
EDIT: Just to clarify, the new FeedObject does an AJAX call.
What you need to determine when new FeedObject is done. Depending on what FeedObject provides, it could mean:
Providing a callback that it called when new FeedObject is done. Call done in the callback.
Listening to an event (most likely a custom jQuery event). Call done in the event handler.
Make the before hook return a promise that exists on the object returned by new FeedObject. For instance, the ready field could contain this promise, and you'd do return myComponent.ready. Mocha will wait for the promise to be resolved. (Remove the done argument from your before hook.)
I strongly suggest that you add one of the facilities above rather than use setTimeout. The problem with setTimeout is that it is always suboptimal: either you wait longer than you need for the DOM to be ready, or you set a timeout which is super close to the real time it take to be ready but in some cases the timeout will be too short (e.g. if your machine happens to have a spike of activity that does not leave enough CPU for the browser running the test code) and the test will fail for bogus reasons.
Well setTimeOut will work (until the server response takes longer than the time you specified and then it won’t work) but it’s definitely not the best or fastest way to do it. When you’re using an async call, you need to define a callback method that runs after the code is finished. The idea of the code should be something like this:
describe('Default Case', function () {
// Your code goes here
$.ajax = function(ajaxOpts)
{
var doneCallback = ajaxOpts.done;
doneCallback(simulatedAjaxResponse);
};
function fetchCallback(user)
{
expect(user.fullName).to.equal("Tomas Jakobsen");
done();
};
fetchCurrentUser(fetchCallback);
});
If you need even more details you could check this very helpful link. Hope that helps!
I would use this async chai plugin. If you are using a promise api (probably the most straight-forward way to handle async testing), this makes testing async ops very simple. You just have to remember to return any invocation that is async (or returns a promise) so chai knows to 'wait' before continuing.
describe(() => {
let sut
beforeEach(() => {
sut = new FeedObject()
return sut.createDomElements() // <- a promise
})
it('should exist', () => {
$('#target').find('.feed-item').should.exist
})
})
Also consider the goals of this testing: why are you doing it? I find that a lot of DOM insert/remove/exists testing is wasted effort. This is particularly true if you are testing a 3rd party library/framework as part of your application code. The burden of proof is on the library/framework to prove it is correct (and most well-written libraries already have a testing suite), not your app. If you are concerned about testing 'did I correctly invoke this 3rd party code' there are better ways to do that without touching the DOM.

Rejected promises in Protractor/WebDriverJS

WebDriverJS and Protractor itself are entirely based on the concept of promises:
WebDriverJS (and thus, Protractor) APIs are entirely asynchronous. All
functions return promises.
WebDriverJS maintains a queue of pending promises, called the control
flow, to keep execution organized.
And, according to the definition:
A promise is an object that represents a value, or the eventual
computation of a value. Every promise starts in a pending state and
may either be successfully resolved with a value or it may be rejected
to designate an error.
The last part about the promise rejection is something I don't entirely understand and haven't dealt with in Protractor. A common pattern we've seen and written is using then() and providing a function for a successfully resolved promise:
element(by.css("#myid")).getAttribute("value").then(function (value) {
// do smth with the value
});
The Question:
Is it possible that a promise returned by any of the Protractor/WebDriverJS functions would not be successfully resolved and would be rejected? Should we actually worry about it and handle it?
I've experienced a use-case of promise rejection while using browser.wait(). Here is an example:
var EC = protractor.ExpectedConditions;
function isElementVisible() {
var el = element(by.css('#myel'));
// return promise
return browser.wait(EC.visibilityOf(el), 1000)
.then(function success() {
return true; // return if promise resolved
}, function fail() {
return false; // return if promise rejected
});
}
expect(isElementVisible()).toBe(true);
expect(isElementVisible()).toBe(false);
Here, if element is on a page, success will be executed, otherwise, if it is not found when 1 second passes, then fail will be called. My first point is that providing a callback for rejection gives an ability to be consistent with what one should expect. In this case I am kinda sure that promise will always resolve to true or false, so I can build a suite relying on it. If I do not provide a fail callback, then I'll get an Uncaught exception because of timeout, which will still fail my particular spec and still run the rest of the specs. It won't be uncaught by the way, Protractor is gonna catch it, but here I want to bring a second point, that Protractor is considered a tool which you use to write and run your code, and if an exception is caught by Protractor, then this exception has left your code unhandled and your code has a leak. But ... at the same time I do not think that one should waste time to catch everything in tests: if there is no element on a page or click has failed, then a respective spec will obviously fail too, which is fine in most of the cases. Unless you want to use the result of failure to build some code on top of it like it is in my sample.
That is the great thing about promises you are going to get a response, either an response of data or an error message. That extended to a series of promises like Webdriver uses you are going to get an array of responses or a failure response of the first one that fails. How you handle the failed response is up to you I usually just dump it into a log for the console to see what failed. The only thing you need to figure out is do you abort the rest of your tests or do you continue on.
Here is a decent article on the subject as well. http://www.toolsqa.com/selenium-webdriver/exception-handling-selenium-webdriver/
Fyi what you are doing is fine you are just never bothering to catch any of the errors though, I am not sure if that matters to you or not, you could also abstract the call in a function to auto handle the errors for you if you wanted to log them somewhere.

errors not being thrown after promise

i have a weird situation i would like to know how to solve.
in my app errors that happen after functions with promises do net get thrown and the app just stalls.
here an example:
getTenant = (req) ->
deferred = Q.defer()
deferred.resolve('foo') if req.info.referrer
deferred.resolve('bar') if !req.info.referrer
deferred.promise
Routes =[
{
method: 'GET'
path: '/'
handler: (request, reply) ->
getTenant(request).then (tenant) ->
console.log 'tenant', tenant
# here `User` is not defined and doesn't even exist
# why is there no error here?
if !User.isAuthorized(request, tenant)
reply 'not authorized'
else
reply 'authorized'
}
]
after getTenant i call a function on User.
User doesn't exist or is imported but the app gives me no error.
why is that?
of course if i wrap the code in a try/catch i catch the error but thats not the point. i would expect the code to actually break and throw the error.
here the full sample app: https://github.com/aschmid/hapierrortest
thank you,
andreas
The short answer is because you have neglected to include your error handler. Promises always require both success and error handling functions.
This is a fundamental (and generally good) aspect of promises. Inside a promise handler (success or error), any thrown errors do not hit the window. Instead, the promise implementation internally wraps promise handlers in a try block, and upon capture, rejects the promise returned by .then. Why? So that you can choose where and when and how to deal with errors. This is why you can put a single catch(errHandler) (which is sugar for .then(null, errHandler)) at the end of a long promise chain:
somePromise.then(function s1 (val1){
// use val1, return something (e.g. a new promise)
}).then(function s2 (val2){
// use val2, return something (e.g. a new promise)
}).then(function s3 (val3){
// use val3, return something (e.g. a new promise)
}).catch(function e1 (err1){
// handle e1 as you like (console.log, render something on the UI, etc.)
});
In the above chain, an error that occurs at somePromise, s1, s2, or s3 will be handled at e1. This is pretty nice, better than having to handle the potential error explicitly at every step of the chain. You still CAN handle the error at every step if you want, but it's not at all necessary.
Generally with promises you'll handle the error in some way, you don't WANT errors to crash your app after all. But if for some reason you wanted errors to be re-thrown out to the outer scope, you'd have to use a promise library with a final method that allows for re-throwing. For example, .done() in the Q library.
I'll start by saying that I've never used Hapi, but the syntax you have looks fine EXCEPT...
If req.info is null, you'd get an exception thrown. Try this
deferred.resolve('foo') if req.info && req.info.referrer
deferred.resolve('bar') if !req.info || !req.info.referrer

Skipping promise chain after handling error

Using the https://github.com/kriskowal/q library, I'm wondering if it's possible to do something like this:
// Module A
function moduleA_exportedFunction() {
return promiseReturningService().then(function(serviceResults) {
if (serviceResults.areGood) {
// We can continue with the rest of the promise chain
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the promise chain
}
});
}
// Module B
moduleA_exportedFunction()
.then(moduleB_function)
.then(moduleB_anotherFunction)
.fail(function(reason) {
// Handle the reason in a general way which is ok for module B functions
})
.done()
;
Basically if the service results are bad, I'd like to handle the failure in module A, using logic that is specific to the internals of module A, but still skip the remaining module B functions in the promise chain.
The obvious solution for skipping module B functions is to throw an error/reason from module A. However, then I would need to handle that in module B. And ideally I'd like to do it without needing any extra code in module B for that.
Which may very well be impossible :) Or against some design principles of Q.
In which case, what kind of alternatives would you suggest?
I have two approaches in mind, but both have their disadvantages:
Throw a specific error from module A and add specific handling code to module B:
.fail(function(reason) {
if (reason is specificError) {
performVerySpecificErrorHandling();
}
else {
// Handle the reason in a general way which is ok for module B functions
}
})
Perform the custom error handling in module A, then after handling the error, throw a fake rejection reason. In module B, add a condition to ignore the fake reason:
.fail(function(reason) {
if (reason is fakeReason) {
// Skip handling
}
else {
// Handle the reason in a general way which is ok for module B functions
}
})
Solution 1 requires adding module A specific code to module B.
Solution 2 solves this, but the whole fake rejection approach seems very hackish.
Can you recommend other solutions?
Let's talk about control constructs.
In JavaScript, code flows in two ways when you call a function.
It can return a value to the caller, indicating that it completed successfully.
It can throw an error to the caller, indicating that an exceptional operation occurred.
It looks something like:
function doSomething(){ // every function ever
if(somethingBad) throw new Error("Error operating");
return value; // successful completion.
}
try{
doSomething();
console.log("Success");
} catch (e){
console.log("Boo");
}
Promises model this exact same behavior.
In Promises, code flows in exactly two ways when you call a function in a .then handler:
It can return a promise or a value which indicates that it completed successfully.
It can throw an error which indicates that an exceptional state occured.
It looks something like:
var doSomething = Promise.method(function(){
if(somethingBad) throw new Error("Error operating");
return someEventualValue(); // a direct value works here too
}); // See note, in Q you'd return Q.reject()
Promise.try(function(){ // in Q that's Q().then
doSomething();
console.log("Success");
}).catch(function(e){
console.log("Boo");
});
Promises model flow of control itself
A promise is an abstraction over the notion sequencing operations itself. It describes how control passes from one statement from another. You can consider .then an abstraction over a semicolon.
Let's talk about synchronous code
Let's see how synchronous code would look in your case.
function moduleA_exportedFunction() {
var serviceResults = someSynchronousFunction();
if (serviceResults.areGood) {
// We can continue with the rest of our code
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the chain
}
}
So, how continuing with the rest of our code is simply returning. This is the same in synchronous code and in asynchronous code with promises. Performing very specific error handling is also ok.
How would we skip the rest of the code in the synchronous version?
doA();
doB();
doC(); // make doD never execute and not throw an exception
doD();
Well, even if not immediately, there is a rather simple way to make doD never execute by causing doC to enter into an infinite loop:
function doC() {
if (!results.areGood) {
while(true){} // an infinite loop is the synchronous analogy of not continuing
// a promise chain.
}
}
So, it is possible to never resolve a promise - like the other answer suggests - return a pending promise. However, that is extremely poor flow control since the intent is poorly conveyed to the consumer and it will be likely very hard to debug. Imagine the following API:
moduleA_exportedFunction - this function makes an API request and returns the service as a ServiceData object if the data is available. Otherwise, it enters the program into an endless loop.
A bit confusing, isn't it :)? However, it actually exists in some places. It's not uncommon to find the following in really old APIs.
some_bad_c_api() - this function foos a bar, on failure it terminates the process.
So, what bothers us about terminating the process in that API anyway?
It's all about responsibility.
It's the responsibility of the called API to convey whether the API request was successful.
It is the responsibility of the caller to decide what to do in each case.
In your case. ModelA is simply breaching the limit of its responsibility, it should not be entitled to make such decisions about the flow of the program. Whoever consumes it should make these decisions.
Throw
The better solution is to throw an error and let the consumer handle it. I'll use Bluebird promises here since they're not only two orders of magnitude faster and have a much more modern API - they also have much much better debugging facilities - in this case - sugar for conditional catches and better stack traces:
moduleA_exportedFunction().then(function(result){
// this will only be reached if no error occured
return someOtherApiCall();
}).then(function(result2){
// this will be called if the above function returned a value that is not a
// rejected promise, you can keep processing here
}).catch(ApiError,function(e){
// an error that is instanceof ApiError will reach here, you can handler all API
// errors from the above `then`s in here. Subclass errors
}).catch(NetworkError,function(e){
// here, let's handle network errors and not `ApiError`s, since we want to handle
// those differently
}).then(function(){
// here we recovered, code that went into an ApiError or NetworkError (assuming
// those catch handlers did not throw) will reach this point.
// Other errors will _still_ not run, we recovered successfully
}).then(function(){
throw new Error(); // unless we explicitly add a `.catch` with no type or with
// an `Error` type, no code in this chain will run anyway.
});
So in a line - you would do what you would do in synchronous code, as is usually the case with promises.
Note Promise.method is just a convenience function Bluebird has for wrapping functions, I just hate synchronous throwing in promise returning APIs as it creates major breakage.
It is kind of a design thing. In general, when a module or service returns a promise, you want it to resolve if the call was successful, and to fail otherwise. Having the promise neither resolve or fail, even though you know the call was unsuccessful, is basically a silent failure.
But hey, I don't know the specifics of your modules, or reasons, so if you do want to fail silently in this case, you can do it by returning an unresolved promise:
// Module A
function moduleA_exportedFunction() {
return promiseReturningService().then(function(serviceResults) {
if (serviceResults.areGood) {
// We can continue with the rest of the promise chain
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the promise chain
return q.defer().promise;
}
});
}
Inspired by Benjamin Gruenbaum's comments and answer - if I was writing this in synchronous code, I would make moduleA_exportedFunction return a shouldContinue boolean.
So with promises, it would basically be something like this (disclaimer: this is psuedo-code-ish and untested)
// Module A
function moduleA_exportedFunction() {
return promiseReturningService().then(function(serviceResults) {
if (serviceResults.areGood) {
// We can continue with the rest of the promise chain
return true;
}
else {
performVerySpecificErrorHandling();
// We want to skip the rest of the promise chain
return false;
}
});
}
// Module B
moduleA_exportedFunction()
.then(function(shouldContinue) {
if (shouldContinue) {
return moduleB_promiseReturningFunction().then(moduleB_anotherFunction);
}
})
.fail(function(reason) {
// Handle the reason in a general way which is ok for module B functions
// (And anything unhandled from module A would still get caught here)
})
.done()
;
It does require some handling code in module B, but the logic is neither specific to module A's internals nor does it involve throwing and ignoring fake errors - mission accomplished! :)

Categories

Resources