Testing a custom method for $state.go - javascript

I tried to test this code:
redireccion() {
this.$state.go('modifyLine', {lineId: this.look()._id});
}
look() {
return Entries.findOne({name: this.entry.name});
}
the code above method is ok (look), but for 'redireccion' I tried something like this and i got an error.
this is the code:
describe('redireccion()', () => {
beforeEach( inject(($state) => {
spyOn($state, 'go');
spyOn(controller, 'look');
spyOn(Entries, 'findOne');
}));
it('should be a ... bla bla', () => {
controller.redireccion();
expect($state.go).toHaveBeenCalledWith('modifyLine', {lineId: });
});
});
This is an excerpt, because really I do not know how testing this.

I will try to give you an insight. You should try to make your tests isolated... That means that if you're testing your redirection, you can mock the look method since it's not relevant (for this specific test).
describe('testing redirection()', () => {
beforeEach( inject(($state) => {
//here I'm saying that I'm spying every call to $state.go
spyOn($state, 'go');
//And here I'm that I'm not only spying every call to
//controller.look() but I'm also replacing the original
//implementation with my fake one. Every call will basically
//return an object with id equals 10
spyOn(controller, 'look').and.callFake(() => {
var mockedLine = {
_id: 10
};
return mockedLine;
});
}));
it('should call state.go', () => {
controller.redireccion();
//if you just want to check if the method was called, do the following:
expect($state.go).toHaveBeenCalled();
//if you need to also check the arguments, try:
var args = $state.go.mostRecentCall.args;
expect(args[0]).toBe('modifyLine');
expect(args[1].lineId).toBe(10);
});
});

Related

Spy on a method of an object created dynamically during test execution

I need to spyOn a method of an object created dynamically inside another method
So consider the following:
public doStuff = () => {
const myThing = new MyThing();
myThing.doSomethingElse().then((data) => {
//do more stuff here...
})
}
I want to therefore spyOn the instance of MyThing and the call to doSomethingElse().
I did come across a solution here which make use of the object's prototype, which I attempted like so
spyOn(MyThing.prototype, 'doSomethingElse').and.returnValue(Promise.resolve({foo: 'bar'}));
But this does not work after I call doStuff() in my tests, I get an error:
Error: : doSomethingElse() method does not exist
But I know this method is fine, since it runs as expected locally.
I am unsure how to proceed, can anyone assist?
Thanks
You might be missing something in your test. This is a simple test and you can see it works as you expected
require("jasmine");
class MyThing {
async doSomethingElse() {
return { bar: "foo" };
}
}
class Stuff {
doStuff() {
const myThing = new MyThing();
myThing.doSomethingElse().then(data => {
console.log(data);
});
}
}
describe("doSomethingElse", () => {
it("toHaveBeenCalled", () => {
spyOn(MyThing.prototype, "doSomethingElse").and.returnValue(
Promise.resolve({ foo: "bar123" })
);
const stuff = new Stuff();
stuff.doStuff();
expect(MyThing.prototype.doSomethingElse).toHaveBeenCalled();
});
});
The key is to use the spyOn before you instantiate the class that contains the doStuff function. If we move the const stuff = new Stuff(); above the spyOn it fails.
Hope it helps
You can create a spy object with mock method by passing in an object where the property names represent returned data for methods.
describe('test', () => {
let mock;
beforeEach(() => {
mock = jasmine.createSpyObj('mock', {
doSomethingElse: Promise.resolve({foo: 'bar'})
});
});
it('call mock', async () => {
const result = await mock.doSomethingElse();
expect(result.foo).toEqual('bar');
});
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine-html.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/boot.js"></script>

How to test if function was called with defined parameters ( toHaveBeenCalledWith ) with Jest

I want to test, if particular function was called in my test and with the correct parameters. From JEST documentation I'm not able to figure out, what is the correct way to do it.
Let's say I have something like this:
// add.js
function child(ch) {
const t = ch + 1;
// no return value here. Function has some other "side effect"
}
function main(a) {
if (a == 2) {
child(a + 2);
}
return a + 1;
}
exports.main = main;
exports.child = child;
Now in unit test:
1.
I want to run main(1) and test that it returned 2 and child() was not called.
2.
And then I want to run main(2) and thest that it returned 3 and child(4) was called exactly once.
I have something like this now:
// add-spec.js
module = require('./add');
describe('main', () => {
it('should add one and not call child Fn', () => {
expect(module.main(1)).toBe(2);
// TODO: child() was not called
});
it('should add one andcall child Fn', () => {
expect(module.main(2)).toBe(3);
// TODO: child() was called with param 4 exactly once
// expect(module.child).toHaveBeenCalledWith(4);
});
});
I'm testing this in https://repl.it/languages/jest , so a working example in this REPL will be much appreciated.
OK, I've figured it out. The trick is, to split functions into separate files. So the code is (and works in https://repl.it/languages/jest ):
// add.js
child = require('./child').child;
function main(a) {
if (a == 2) {
child(a + 2);
}
return a + 1;
}
exports.main = main;
extracted child.js file:
// child.js
function child(ch) {
const t = ch + 1;
// no return value here. Function has some other "side effect"
}
exports.child = child;
main test file:
// add-spec.js
main = require('./add').main;
child = require('./child').child;
child = jest.fn();
describe('main', () => {
it('should add one and not call child Fn', () => {
expect(main(1)).toBe(2);
expect(child).toHaveBeenCalledTimes(0);
});
it('should add one andcall child Fn', () => {
expect(main(2)).toBe(3);
expect(child).toHaveBeenCalledWith(4);
expect(child).toHaveBeenCalledTimes(1);
});
});
let child = require('./child');
let main = require('./add').main;
// name of the module, name of the function
spy = jest.spyOn(child, 'child');
describe('main', () => {
it('should add one and call child Fn', () => {
expect(main(1)).toBe(2);
// Called or not
expect(spy).toHaveBeenCalled();
});
});
In my case I had a similar doubt with angular code so I have a method, that is invoked when a field in a form is changed, and the only task of this method is to trigger some other methods.
Code extract:
handleConnectionToLegChange(value) {
if (!isNullOrUndefined(value)) {
this.connectionsForm.controls.to.markAsDirty();
this.connectionsForm.controls.to.updateValueAndValidity();
this.connectionsForm.controls.from.markAsDirty();
this.connectionsForm.controls.from.updateValueAndValidity();
this.updateModalButtonStatus(this.connectionsSubject);
}}
So in order to test it I used this test case. (I just spied on 2 of the 5 triggered methods but that's enough on my case.)
test extract:
it('should execute fn handleConnectionToLegChange and check method calls if value is not null', () => {
component.connectionsForm.controls.to.updateValueAndValidity = jest.fn();
component.updateModalButtonStatus = jest.fn();
component.handleConnectionToLegChange('a');
expect(component.connectionsForm.controls.to.updateValueAndValidity).toHaveBeenCalled();
expect(component.updateModalButtonStatus).toHaveBeenCalled(); });
It worked fine for me.
To be mocked:
// child.js
function child(ch) {
console.log('some side effects happen in here', ch);
}
exports.child = child;
To be tested:
// main.js
const { child } = require('./child');
function main(a) {
if (a == 2) {
child(a + 2);
}
return a + 1;
}
exports.main = main;
Test for main.js
// main.test.js
jest.mock('./child');
const { main } = require('./main');
// This is the mocked version of "child"
const { child } = require('./child');
describe('main', () => {
it('should add one and not call child Fn', () => {
expect(main(1)).toBe(2);
expect(child).toHaveBeenCalledTimes(0);
});
it('should add one and call child Fn', () => {
expect(main(2)).toBe(3);
expect(child).toHaveBeenCalledWith(4);
expect(child).toHaveBeenCalledTimes(1);
});
});

Testing a method of an object returned by a function

I have a function that returns an object with many methods and I need to check one of the methods inside this returned object. I am using AngularJS and Karma+Jasmine as testing suite. How do I call methods inside the object returned by a function?
function modalOptions() {
.........
return this.$q((resolve) => {
// test accessable here
this.solveModel = {
save: () => {
// test can't call save()
this.saveToDB = this.toSendToDB;
},
cancel: () => { ...
},
delete: () => { ...
}
};
});
}
My test is somewhat like this...
it('should save modal with the data', function() {
scope.$apply();
expect(vm.modalOptions).toBeDefined();
vm.toSendToDB = true; // hard-coded
vm.savedToDB = undefined // default value from other controller
spyOn(vm, 'modalOptions').and.callThrough();
console.log(vm.modalOptions()); // gives weird response: c{$$state: Object{status: 0}} instead of the solveModal object
expect(vm.toSendToDB).toBeTruthy();
expect(vm.savedToDB).toBeTruthy();
});
Sorry, I can not comment yet, but the promise has to be resolved and the solveModel passed to it, in order for solveModel to be returned. Where do you resolve the promise?

How do I stub a chain of methods in Sinon?

I know how to use stub to replace one function.
sandbox.stub(Cars, "findOne",
() => {return car1 });
But now I have a line in my function I want to test that I need to stub that looks like this
Cars.find().fetch()
So there is a chain of function here and I'm unsure what I need to do. How do I stub "find" to return something that I can use to stub "fetch"?
IMHO, we can just use returns to do this. We don't need to use callsFake or mock it as function.
// Cars.find().fetch()
sinon.stub(Cars, 'find').returns({
fetch: sinon.stub().returns(anything)
});
in case, if there is another method after fetch(), we can use returnsThis()
// Cars.find().fetch().where()
sinon.stub(Cars, 'find').returns({
fetch: sinon.stub().returnsThis(),
where: sinon.stub().returns(anything)
});
Ref:
https://sinonjs.org/releases/v6.3.3/
Hope it helps
Try this:
sandbox.stub(Cars, "find", () => {
return {
fetch: sinon.stub().returns(anything);
};
});
The form of attaching a function to a stub shown here:
sandbox.stub(Cars, "find", () => {
return {
fetch: sinon.stub().returns(anything);
};
});
is deprecated.
It's now, as of version 6.3
sandbox.stub(Cars, "find").callsFake(() => {
return {
fetch: sinon.stub().returns(anything);
};
});
This is another approach that also allows spying on chains of jQuery methods - which took me a long time to figure out.
In the example, I am trying to test that an email field is cleared out
//set up stub and spy
const valSpy = sandbox.spy();
const jQueryStub = sandbox
.stub($.prototype, "find") // this prototype is important
.withArgs("input[name=email]")
.returns({ val: valSpy });
// call function under test
learnerAlreadyAccepted(inviteDoc);
// check expectations
expect(jQueryStub).to.have.been.called; // not really necessary
expect(valSpy).to.have.been.calledWith("");
and the function under test is (roughly):
learnerAlreadyAccepted = function(doc) {
$("form").find("input[name=email]").val("");
}
I ran into this problem and, though I liked the solution for a single test, wanted something more dynamic that would allow for reuse across tests. I also preferred the sandbox approach, as it made restoring much easier for larger suites. End result:
export function setupChainedMethodStub(sandbox: sinon.SinonSandbox, obj: any, methodName: string, methodChain: string[], value: any) {
return sandbox.stub(obj, methodName).returns(generateReturns(sandbox, methodChain, value));
}
function generateReturns(sandbox: sinon.SinonSandbox, methodChain: string[], value: any): any {
if (methodChain.length === 1) {
return {
[methodChain[0]]: sandbox.stub().returns(value),
};
} else {
return {
[methodChain[0]]: sandbox.stub().returns(generateReturns(sandbox, methodChain.slice(1), value)),
};
}
}
Wherever I want to set up a stub on the fly, I pass in the created sandbox and the other parameters:
setupChainedMethodStub(sandbox, MyMongooseModel, 'findOne', ['sort', 'exec'], { foo: 'bar' })
Then I just have a sandbox.restore() in my highest scoped afterEach()
There are a few changes from v2.0.
More details here
One of them is:
stub(obj, 'meth', fn) has been removed, see documentation
You can downgrade but I would not recommend it, instead you can do something like this:
let stub = sinon.stub(obj, "meth").callsFake(() => {
return {
meth2: sinon.stub().callsFake(() => {
return {
meth3: sinon.stub().returns(yourFixture),
};
}),
};
});
I have a simple solution that hopefully works for others.
Presuming that fetch is also a method on Cars, and fetch and find support method chaining, Cars may look something like this:
class Cars {
fetch() {
// do stuff
return this;
}
find() {
// do stuff
return this;
}
}
[ANSWER] We should be able to support method chaining with the stub like this:
sandbox.stub(Cars, 'fetch').callsFake(function () { return this; }); // optional
sandbox.stub(Cars, 'findOne').callsFake(function () { return this; });

Jasmine does not reset spy after each test spec

I have the following spec.
describe("SN.ExitHistory", function() {
var exitHistory;
beforeEach(function() {
SN.Utils = jasmine.createSpy("utils").andCallFake(function() {
function readSNCookie(cookieName, key) {
return "google.com";
}
function isUndefinedOrNull(param) {
return (param == null) || (param === "null");
}
function createSNCookie(snCookieName, key, value, lifeTime) {
}
var me = {
readSNCookie : readSNCookie,
isUndefinedOrNull : isUndefinedOrNull,
createSNCookie : createSNCookie
};
return me;
})();
exitHistory = SN.ExitHistory();
});
it("return last exit link", function() {
expect(exitHistory.getLastExitLink()).toEqual("google.com");
});
});
exitHistory.getLastExitLink internally use SN.Utils.
After the test is done Jasmine does not remove the spy object utils. In next test suite also I can see the same utils present. Is there any way to reset the spy object after each test is done?
Instead of creating spy, if I create a new object for utils, behavior is same. Then what is the difference between a spy and actual object in this scenario.
Correct me if I am wrong.
I had the same problem some time ago and after days of struggling I found the solution. If you use the other way your spy will be reseted, so try with
spyOn(SN, 'Utils');
Spies are described here:
https://github.com/pivotal/jasmine/wiki/Spies
Use spyOn and declare your spies within a before block inside of a describe spec block and the spies will be cleaned up when each spec is torn down.
aSpec.js
describe('something', () => {
beforeAll(() => spyOn(someObject, 'someMethod').and.returnValue('foo'));
it('is spied on', () => {
expect(someObject.someMethod()).toEqual('foo');
});
});
anotherSpec.js
describe('something else', () => {
beforeAll(() => spyOn(someObject, 'someMethod').and.returnValue('bar'));
it('is spied on', () => {
expect(someObject.someMethod()).toEqual('bar');
});
});

Categories

Resources