Mocha test will not resolve promise using Sinon stub - unit-testing

I'm using Mocha to test a method that has an asynchronous method inside of it. I'm stubbing that dependency with Sinon, and returning a resolved promise. But the promise is never resolved, or at least when the assertion is run it hasn't resolved yet.
Here is the method under test
function identify(traits) {
//THIS GETS CALLED SUCCESSFULLY
userService.get().then(function(user){
//CODE NEVER REACHES HERE
userService.set(user).then(function(){
//do something
}, function(){
//handle error
});
});
}
And here is the test
it('should identify a valid email address', function(){
var user = { email: 'test#example.com' };
var getUserStub = sinon.stub(userService, "get");
var setUserStub = sinon.stub(userService, "set");
var userReturn = { email: 'test#example.com', urls: ['http://some.url.com'] };
getUserStub.returns(Promise.resolve(userReturn));
//THE METHOD UNDER TEST
identifyController.identify(user);
sinon.assert.calledOnce(userService.get); //WORKS FINE
sinon.assert.calledOnce(userService.set); //FAILS
getUserStub.restore();
});
The assertion on userService.set fails, it says it was called 0 times. What am I doing wrong?

I've finally found the problem.
Promises are essentially asynchronous, but sinon calls are synchronous.
See this code pen.
What happens:
You call identifyController.identify(user);
It will call get, which returns a promise, which is asyncrhonous.
The main thread of the program will still be running, so your both sinon.assert.calledOnce calls will happen in sequence, synchronously
By the time get resolves itself and calls set, because promises are non-blocking, the assertion will already have been executed, so it will fail.
So, you can do like this:
function identify(traits) {
return userService.get().then(function(user){
console.log('get');
userService.set(user).then(function(){
console.log('set');
//do something
});
});
}
And change this:
identify(user).then(function(){
sinon.assert.calledOnce(myObj.get); //WORKS FINE
sinon.assert.calledOnce(myObj.set); //WORKS FINE NOW
});

Related

toHaveBeenCalled() failing when mocked output shows correct data

I am getting tired of trying to figure out the following out. Basically I have a method in my component that calls a service which is mocked. Once that service completes, another service does some logging activities, which is also mocked. But my test fails saying the logging service wasn't called:
process(){
const that : any = this;
this.mainService.process().then(result=>{
return result;
}).then(result=>{
//log the operation now after doing some checkups
let checkups = ""
that.logService.log('process',result, checkups).then(logged=>{
console.log(logged)
}).catch(err=>console.log(err)
}).catch(err=>console.log(err);
}
Before we go ahead, doing Promise.all() isn't match of an option due to the logic in place to do checkups. Now to the testing bit:
fit("should log processed request", done => {
const mainSerivce = TestBed.get(MainService)
const logService = TestBed.get(LogService)
spyOn(mainService, "process").and.returnValue(Promise.resolve({id:34,value:64, rank:310));
const logSpy = spyOn(logSerivce, "log").and.returnValue(Promise.resolve({'done':true}))
fixture.whenStable().then(finished=>{
component.process();
expect(logSpy).toHaveBeenCalled();
done();
})
});
expect(logSpy).toHaveBeenCalled();
fails now but I can see in my console the result of the mock {'done':true} or whatever value I pass is shown, meaning it was mocked and called (?). What am I missing exactly since the methods appear to have been mocked and logged correctly in the console.
It seems to me you have to wait for the promises to resolve before asserting for it, try:
fit("should log processed request", done => {
const mainSerivce = TestBed.get(MainService)
const logService = TestBed.get(LogService)
spyOn(mainService, "process").and.returnValue(Promise.resolve({id:34,value:64, rank:310));
const logSpy = spyOn(logSerivce, "log").and.returnValue(Promise.resolve({'done':true}));
// call the function that will resolve promises
component.process();
// whenStable waits for the promises to resolve.
fixture.whenStable().then(finished=>{
console.log('Making the assertion !!');
expect(logSpy).toHaveBeenCalled();
done();
});
});
Make sure you see the log of { 'done': true } before the log of Making the assertion !!. But since you have a promise resolving within a promise, the following might fix it.
fit("should log processed request", async done => { // check out the async keyword here
const mainSerivce = TestBed.get(MainService)
const logService = TestBed.get(LogService)
spyOn(mainService, "process").and.returnValue(Promise.resolve({id:34,value:64, rank:310));
const logSpy = spyOn(logSerivce, "log").and.returnValue(Promise.resolve({'done':true}));
// call the function that will resolve promises
component.process();
// when stable waits for the promises to resolve.
await fixture.whenStable();
await fixture.whenStable();
expect(logSpy).toHaveBeenCalled();
done();
});

Jest coverage testing global objects and inverse condition

I've got a global object on window that has a function. In my code I'm writing this:
if (window.foo) {
window.foo.bar();
}
In the tests, when window.foo has a value I assert that window.foo.bar has been called. Easy enough.
Jest coverage is complaining that I'm not testing the negative value, i.e. when window.foo is undefined. But I've been struggling to work out what to assert on.
What I'd like to do - is mock window.foo and assert that it is only called once, when we check whether it has a value or is undefined (i.e. the call to window.foo.bar is never made.
I'm trying to mock the global window object and return an object but I'm getting confused as to how to mock and spyOn a value when it isn't a function, and then check it has been accessed.
Any help appreciated!
You could use a getter so whenever a property in the object is being accessed inside the getter we could run multiple actions, in our case we just trigger our spy manually.
describe('window.foo', () => {
afterEach(() => {
delete global.foo // make sure you clean your global object after each test
})
it('should be called twice', () => {
const fooSpy = jest.fn();
const barSpy = jest.fn();
Object.defineProperty(global, 'foo', {
configurable: true, // very important or else you can't delete
get() {
fooSpy(); //we manually call our spy so we can assert later
// and we return an object with another spy for our bar function
return { bar: barSpy};
}
});
if (global.foo) {
global.foo.bar()
}
expect(fooSpy).toHaveBeenCalledTimes(2);
expect(barSpy).toHaveBeenCalledTimes(1);
});
it('should be called once', () => {
const fooSpy = jest.fn();
Object.defineProperty(global, 'foo', {
writconfigurableable: true,
get() {
fooSpy(); // we trigger the spy manually
// we return undefined
return undefined;
}
});
if (global.foo) {
global.foo.bar()
}
expect(fooSpy).toHaveBeenCalledTimes(1);
});
});
You can see it working in here

Test when eventEmitter subscription has finished

I have a function like:
public openCamera = (obj) => {
this._nativeCamera.getPicture()
.subscribe((selectedImage) => {
obj.avatar = selectedImage;
});
};
Now I want to test this function.
it('should pass', function(){
let e = new EventEmitter();
let obj = {};
spyOn(mockNativeCamera,'getPicture').and.callFake(()=>e);
sut.openCamera(obj);
e.emit('Hello Dolly');
expect(obj.avatar).toBe('Hello Dolly'); // This should be checked after subscription has finished, but happens synchronously and thus fails
});
Problem is that although this test should be async, I do not know how to hook to the execution of the asynchronous execution.
I thought of subscribing to the event emitter inside the test, but there is no guarantee which subscription callback will be called first, so this sounds like a bad idea.
Any thoughts?

Jasmine spyOn not working

I am fairly new to Jasmine, and I have to test a few function calls:
JS CODE
object1 = {
function1: function() {
// object1.function2 is a callback
object2.someFunction("called", object1.function2)
},
function2: function() {
// code to do stuff
}
}
TEST CODE
describe("test suite", function(){
it("test1", function(){
spyOn(object1, "function2");
object1.function1();
expect(object1.function2).toHaveBeenCalled();
});
});
I've tried the above but it fails, and says "Expected spy function2 to have been called". Can somebody help me out with this ? Thanks
You can rewrite the test as follows
describe("test suite", function(){
it("test1", function(done){
spyOn(object1, "function2");
object1.function1();
setTimeout(function() {
expect(object1.function2).toHaveBeenCalled();
done();
});
});
});
Your test code needs to have asynchronous testing since the callback will never be called immediately. You can add another async call which will be placed after your object1.function2 in the call stack and by the time the function inside setTimeout is executed it would have already called the object1.function2 and once assertion is made you can end the async test by calling done().

Qooxdoo: How to call MMock.verify() in an asynchronous test

I'm trying to use qx.dev.unit.MMock.verify() in an asynchronous situation effectively the same as this:
, test_no_output_from_verify: function () {
var mocker = this.mock({ callMeOnce: function () { } });
mocker.expects('callMeOnce').once();
qx.event.Timer.once(function () {
mocker.verify();
this.resume();
}, this, 100);
this.wait(1000);
}
I find that, when the verify() call throws an exception (because the callMeOnce function was not called), that exception is swallowed by the Test Runner. It carries on waiting for the 1000ms timeout to elapse, and then throws the standard timeout exception: 'Asynchronous Test Error: Timeout reached before resume() was called.'.
Is there any way that I can make Test Runner halt immediately and display the exception thrown from mocker.verify() instead?
==============================
EDIT: Reading the manual one more time after posting the question shows my mistake. The correct technique is to put the verify() call inside the resume(), thus:
, test_no_output_from_verify: function () {
var mocker = this.mock({ callMeOnce: function () { } });
mocker.expects('callMeOnce').once();
qx.event.Timer.once(function () {
this.resume(function () {
mocker.verify();
});
}, this, 100);
this.wait(1000);
}
This works as expected and I get the ExpectationError: Expected callMeOnce([...]) once (never called) message inside the Test Runner.