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);
});
Related
I have a test that creates a controller like such...
this.createScope = function(scope) {
if (scope) {
this.scope = scope;
} else {
this.scope = $rootScope.$new();
}
this.controller = $controller("menuController", {
"$scope": this.scope,
updateActionList: function() {
return {
finally: function() {}
};
}
});
};
I added this part...
updateActionList: function() {
return {
finally: function() {}
};
}
Because when I run my tests, all of them fail because....
TypeError: undefined is not an object (evaluating 'updateActionList().finally')
updateActionList() is a local function that is called in the actual code like this...
updateActionList().finally(function() {
//Do stuff
});
updateActionList() returns a promise from getThings() with a .then and a .finally blocks.
I just want the finally block to resolve itself really so the tests can pass.
Or is there some other thing I need to do? I'm not sure why the finally is undefined.
So this call...
updateActionList().finally(function() {
//Do stuff
});
returns a promise from some other function, essentially my problem was that the function returning the promise before updateActionList() mock needed another finally call.
this.getThings = jasmine.createSpy("getThings").and.returnValue({
then: function(cb) {
cb(self.mockPlugins);
return {
finally: function(cb) {
cb();
return {
finally: function(cb) {
cb();
}
};
}
};
}
});
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).
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.
I'm testing an AngularJS service, and as part of it, I want to make sure a certain callback is never called. Right now, my test looks like
it('should succeed to login the user', function () {
var params = {
email: 'foo#bar.com',
password: 'hunter2'
};
var member = {
remoteAddress: '1.2.3.4'
};
$httpBackend.expectPOST(fakeApiUrl + '/1/authentication/login', params)
.respond(200, member);
auth.login(params.email, params.password).then(
function (m) {
expect(m.remoteAddress).toBe(member.remoteAddress);
},
function () {
// I want this to be fail()
expect(true).toBe(false);
}
);
$rootScope.$digest();
$httpBackend.flush();
});
The problem is, in order to make the test fail if that callback is triggered, I have to do something dirty like expect(true).toBe(false).
Is there a better way to accomplish this? For example, is there a more appropriate matcher I could be using, or should I be structuring my test differently?
One way to do it is to create a spy and assert that the spy was never called...
var mySpy = jasmine.createSpy('mySpy');
$httpBackend.expectPOST(fakeApiUrl + '/1/authentication/login', params)
.respond(200, member);
auth.login(params.email, params.password).then(
function (m) {
expect(m.remoteAddress).toBe(member.remoteAddress);
},
mySpy
);
$rootScope.$digest();
$httpBackend.flush();
expect(mySpy).not.toHaveBeenCalled();
it('should succeed to login the user', function () {
var params = {
email: 'foo#bar.com',
password: 'hunter2'
};
auth.login = jasmine.createSpy().andReturn($q.reject());
auth.login(params.email, params.password).then(
function (m) {
expect(m.remoteAddress).toBe(member.remoteAddress);
},
function () {
// I want this to be fail()
expect(true).toBe(false);
});
$scope.$digest();
});
http://jsfiddle.net/zk8Lg/1/