I have 2 functions where one calls the other and the other returns something, but I cannot get the test to work.
Using expect(x).toHaveBeenCalledWith(someParams); expects a spy to be used, but I am unaware of how to spy on a function within the same file...
Error: : Expected a spy, but got Function.
Usage: expect().toHaveBeenCalledWith(...arguments)
Example.ts
doSomething(word: string) {
if (word === 'hello') {
return this.somethingElse(1);
}
return;
}
somethingElse(num: number) {
var x = { "num": num };
return x;
}
Example.spec.ts
fake = {"num": "1"};
it('should call somethingElse', () => {
component.doSomething('hello');
expect(component.somethingElse).toHaveBeenCalledWith(1);
});
it('should return object', () => {
expect(component.somethingElse(1)).toEqual(fake);
});
In your Example.spec.ts, just add a spyOn(component, 'somethingElse'); as first line of your it('should call somethingElse ... test :
fake = {"num": "1"};
it('should call somethingElse', () => {
// Add the line below.
spyOn(component, 'somethingElse');
component.doSomething('hello');
expect(component.somethingElse).toHaveBeenCalledWith(1);
});
it('should return object', () => {
expect(component.somethingElse(1)).toEqual(fake);
});
The expect method needs a Spy as parameter when used before a toHaveBeenCalledWith (as per the Jasmine documentation).
Related
I am currently writing unit tests to improve my coverage and I am stuck on a function where I want to set the input parameter of a function.
The function I want to test is:
this.map.forEachFeatureAtPixel(e.pixel, (event: any) => {
if (
event.values_.name === 'name'
) {
this.openWeatherData(event.values_.name);
} //more logic ...
});
I want to test the code inside the callback function to test if the correct calls are made.
But how do I set the event parameter to something like
{ values_ : { name: 'name' } }
and execute the actual callback implementation to improve my coverage.
Use jest.spyOn(object, methodName) to create a mock version for this.map.forEachFeatureAtPixel() method and its mocked implementation. The mocked implementation accept two paramters: pixel and callback. Invoke the callback function manually with your mocked event object.
E.g.
index.ts:
export const obj = {
map: {
// simulate real implementation
forEachFeatureAtPixel(pixel, callback) {
callback();
},
},
method(e) {
this.map.forEachFeatureAtPixel(e.pixel, (event: any) => {
if (event.values_.name === 'name') {
console.log('openWeatherData');
}
});
},
};
index.test.ts:
import { obj } from './';
describe('67818053', () => {
it('should pass', () => {
const mEvent = { values_: { name: 'name' } };
const forEachFeatureAtPixelSpy = jest
.spyOn(obj.map, 'forEachFeatureAtPixel')
.mockImplementationOnce((pixel, callback) => {
callback(mEvent);
});
obj.method({ pixel: '12' });
expect(forEachFeatureAtPixelSpy).toBeCalledWith('12', expect.any(Function));
forEachFeatureAtPixelSpy.mockRestore();
});
});
test result:
PASS examples/67818053/index.test.ts (8.297 s)
67818053
✓ should pass (14 ms)
console.log
openWeatherData
at examples/67818053/index.ts:11:17
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 9.055 s
Here is my js code
launchTask(taskId)
{
const taskIds = window.external.MyObjectFactory("INDEXED");
taskIds.add(taskId);
}
And here is how i am trying to create a spy and writing my spec for the above functon.
describe("The launchTask function", () => {
beforeEach(() => {
global.external.MyObjectFactory= jasmine.any(Function);
spyOn(global.external, 'MyObjectFactory').and.callThrough();
jasmine.createSpyObj("global.external.MyObjectFactory", ["add"]);
});
it("Scene 1", () => {
launchTask(123);
expect(global.external.MyObjectFactory).toHaveBeenCalledWith("INDEXED")
expect(global.external.MyObjectFactory("INDEXED").add).toHaveBeenCalledWith(123)
});
});
The first expect is passing without any errors where as the second expect is giving me an error "plan.apply is not a function"
You haven't actually attached the function add() to MyObjectFactory. Try something like this:
describe("The launchTask function", () => {
let spyObj;
beforeEach(() => {
global.external.MyObjectFactory= jasmine.any(Function);
spyObj = jasmine.createSpyObj(["add"]);
spyOn(global.external, 'MyObjectFactory').and.returnValue(spyObj);
});
it("Scene 1", () => {
launchTask(123);
expect(global.external.MyObjectFactory).toHaveBeenCalledWith("INDEXED");
expect(spyObj.add).toHaveBeenCalledWith(123);
});
});
Say that you have spyOn(obj, 'method').and.callFake(fn);. How can you subsequently revert obj.method back to it's original function?
Use case: if you are doing a callFake in a big beforeEach and want to use the original method for one of your test cases, but the fake in the rest.
test.js
var obj = {
method: function () {
return 'original';
},
}
module.exports = obj;
testSpec.js
var obj = require('../test.js');
describe('obj.method', function () {
it('should return "original" by default', function () {
expect(obj.method()).toBe('original');
});
it('should return "fake" when faked', function () {
spyOn(obj, 'method').and.callFake(function () {
return 'fake';
});
expect(obj.method()).toBe('fake');
});
it('should return "original" when reverted after being faked', function () {
spyOn(obj, 'method').and.callFake(function () {
return 'fake';
});
// what code can be written here to get the test to pass?
expect(obj.method()).toBe('original');
});
});
I'm using Jasmine v2.5.2.
Edit: Well, I suppose you could just write:
obj.method = function () {
return 'original';
};
but that feels way too not-DRY. Is there something jasmine-based like obj.method.revertToOriginal()?
You can call callThrough() on spied method to revert it to basic function.
var obj = {
method: function() {
return 'original'
}
}
describe('obj.method', function() {
it('should return "original" by default', function() {
expect(obj.method()).toBe('original');
});
it('should return "fake" when faked', function() {
spyOn(obj, 'method').and.callFake(function() {
return 'fake';
});
expect(obj.method()).toBe('fake');
});
it('should return "original" when reverted after being faked', function() {
spyOn(obj, 'method').and.callFake(function() {
return 'fake';
});
obj.method.and.callThrough() // method for revert spy
expect(obj.method()).toBe('original');
});
});
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
Hi I have a Angular service that uses another service that loads data from the local storage on init.
angular
.module('app')
.factory('localStorage', function ($window)
{
if (!$window.localStorage)
{
// throw Error
}
return $window.localStorage;
});
angular
.module('app')
.factory('session', function (localStorage)
{
var container = JSON.parse(localStorage.getItem('sessionContainer'));
return {
getUser: getUser
};
});
Now i want to test the session service.
describe('SessionService', function ()
{
var service;
var localStorageMock;
// Load the module.
beforeEach(module('appRegistration'));
// Create mocks.
beforeEach(function ()
{
logMock = {};
localStorageMock = jasmine.createSpyObj('localStorageServiceMockSpy', ['setItem', 'getItem']);
localStorageMock.getItem.and.returnValue('{}');
module(function ($provide)
{
$provide.value('localStorage', localStorageMock);
});
inject(function (_session_)
{
service = _session_;
});
});
it('should call `getItem` on the `localStorageService` service', function ()
{
expect(localStorageMock.getItem).toHaveBeenCalledWith('sessionContainer');
});
describe('getUser method', function ()
{
it('should return an empty object when the user is not set', function ()
{
var result = service.getUser();
expect(result).toEqual({});
});
it('should return the user data', function ()
{
// localStorageMock.getItem.and.returnValue('{"user":{"some":"data"}}');
var result = service.getUser();
expect(result).toEqual({some: 'user data'});
});
});
});
As you can see in the should return the user data section.
I need a way to update the container so getUser returns the expected data.
I tried to update the getItem spy, but this does not work. The localStorageMock is already injected in the session service when i want to change the spy.
Any help?
The most simple way is to have a variable with mocked value that is common for both function scopes:
var getItemValue;
beforeEach({
localStorage: {
getItem: jasmine.createSpy().and.callFake(function () {
return getItemValue;
}),
setItem: jasmine.createSpy()
}
});
...
it('should return the user data', function ()
{
getItemValue = '{"user":{"some":"data"}}';
inject(function (_session_) {
service = _session_;
});
var result = service.getUser();
expect(result).toEqual({some: 'user data'});
});
Notice that inject should be moved from beforeEach to it for all specs (the specs that don't involve getItemValue may use shorter syntax, it('...', inject(function (session) { ... }))).
This reveals the flaw in service design that makes it test-unfriendly.
The solution is to make container lazily evaluated, so there is time to mock it after the app was bootstrapped with inject:
.factory('session', function (localStorage)
{
var containerCache;
function getUser() {
...
return this.container;
}
return {
get container() {
return (containerCache === undefined)
? (containerCache = JSON.parse(localStorage.getItem('sessionContainer')))
: containerCache;
},
getUser: getUser
};
});
Additionally, this makes possible to test session.container as well. In this case localStorageMock.getItem spy value may be redefined whenever needed.
Folks,
Lets say I have the following function. What would be a proper way to write a Spy, or any other method of testing this with Jasmine?
var Ideas = require('../models/Ideas').Ideas;
var doSomething = function doSomething(req, rsp, userId) {
controllerHelper.batchGet(req, rsp,
function(ids) { return Ideas.get(ids, userId); },
function(tags) { return Ideas.getTags(tags, userId); },
function(tags) { return Ideas.getSpecificTags(tags, userId); },
function() { return Ideas.getAll(userId); });
};
Thanks!
If you want to test if the function has been called or with what arguments it was called you can use jasmine.createSpy()...
it("should test your function", function () {
doSomething = jasmine.createSpy();
doSomething(1,2,3);
expect(doSomething).toHaveBeenCalled();
expect(doSomething).toHaveBeenCalledWith(1,2,3);
});
If you want to test the return result of the function you can just call it in your expect...
it("should test your function", function () {
expect(doSomething(req, rsp, userId)).toEqual(expectedResult);
});