Jest coverage testing global objects and inverse condition - unit-testing

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

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();
});

Redux Saga: Throw and stop generator

I'm writing a generator. I'm testing it with RITEway. It checks if window.ethereum is defined. If its not, it should throw and stop. Basically it should satisfy the following tests:
describe('handle initialize Web3 saga', async assert => {
global.window = {}
assert({
given: 'nothing, the window object',
should: 'have no property called Web3',
actual: window.web3,
expected: undefined
})
const gen = cloneableGenerator(handleInitializeWeb3)()
{
// The important parts are in this block scope
const clone = gen.clone()
assert({
given: 'window.ethereum undefined',
should: 'throw',
actual: clone.next().value.message,
expected: '[WARNING]: window.ethereum has no provider!'
})
assert({
given: 'nothing',
should: 'be done',
actual: clone.next().done,
expected: true
})
}
class Provider {}
window.ethereum = new Provider()
// ... more tests
})
Here is how I tried implementing it.
function* handleInitializeWeb3() {
if (!window.ethereum) {
yield new Error('[WARNING]: window.ethereum has no provider!')
}
// ... more yields
}
but this saga doesn't stop. The test where it should: 'be done' fails and the saga gives back the values from the yields outside of the if statement. How can I have these tests pass and the saga stop when the error is thrown?
yielding an error instance acts the same as yielding any other value (i.e. the generator keeps running). If you want to stop the generator you should throw new Error(... like in a normal function.
If for some reason you don't want to throw and do in fact want to yield an error instance and then stop, simply return; after you've yielded the error.

Mocha test will not resolve promise using Sinon stub

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
});

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().

Injection in the setUp() method causes the framework not to wait for the Future to complete

I am testing an AngularDart component. I am trying to fetch the template and put it in TemplateCache in the setUp() method. For this I need to inject the template cache. However the inject in the setUp() makes the framework continue to the test method and not waiting for the Future to complete. Here is a simplified example.
import 'dart:async';
import 'package:angular/angular.dart';
import 'package:mock/mock.dart';
import 'package:unittest/unittest.dart';
import 'package:angular/mock/test_injection.dart';
import 'package:angular/mock/module.dart';
import 'package:di/di.dart';
class MyTest {
static main() {
group("SetUp with future that waits", () {
setUp(() {
return new Future.value("First").then((_) => print(_));
});
test("Welcome to the world of tomorrow!", () {
print("Second");
});
});
group("SetUp with future that doesn't wait", () {
setUp(inject((Injector inject) { // injection causes the test to not wait
return new Future.value("First").then((_) => print(_));
}));
test("Welcome to the world of tomorrow!", () {
print("Second");
});
});
}
}
In the console you can see the printed messages: First, Second, Second, First.
I think it must be that the inject is not returning the Future. What else can I do to both have the framework injecting objects that I need and waiting for the Future in the setUp()?
This is what I needed. The mistake was trying to return something from the inject itself. It is actually as simple as this:
setUp(() {
// ...
inject((Injectable injectable) {
// inject the objects here and save them in variables
});
// work with the variables
return new Future.value("First").then((_) => print(_));
});