Assume we have the following JavaScript code.
object = _.isUndefined(object) ? '' : aDifferentObject.property;
How would we be able to write a test for either scenarios in Jasmine?
Would it require two seperate describe's? Or would we be able to have a ternary conditional in the test itself?
Thanks!
Jeremy
I will use two separate describe like this
// System Under Test
function getObjectValue() {
return _.isUndefined(object) ? '' : aDifferentObject.property;
}
// Tests
describe('when object is undefined', function() {
it('should return empty string', function() {
expect(getObjectValue()).toBe('');
});
});
describe('when object is no undefined', function () {
it('should return property from different object', function () {
expect(getObjectValue()).toBe(property);
});
});
Consider the following case (Angular JS/ES6/Jasmine, Controller 'as' syntax)
Code:
Controller.toggleWidgetView = () => {
Controller.isFullScreenElement() ? Controller.goNormalScreen() : Controller.goFullScreen();
};
Test cases in Jasmine:
describe('.toggleWidgetView()', function() {
it('should call goNormalScreen method', function() {
spyOn(Controller, 'isFullScreenElement').and.callFake(function(){
return true;
});
spyOn(Controller, 'goNormalScreen').and.callThrough();
Controller.toggleWidgetView();
expect(Controller.goNormalScreen).toHaveBeenCalled();
});
it('should call goFullScreen method', function() {
spyOn(Controller, 'isFullScreenElement').and.callFake(function(){
return false;
});
spyOn(Controller, 'goFullScreen').and.callThrough();
Controller.toggleWidgetView();
expect(Controller.goFullScreen).toHaveBeenCalled();
});
});
Both the test cases passed.
Basically we are calling the 'toggleWidgetView' method twice and in each invocation, the condition changes (true/false) as it will in real world.
Related
I've written a unit test for a function that calls the jQuery getJSON method. The only thing that I'm testing is that it is called with the expected URL. My unit test uses a Jasmine Spy to mock the API call. However, when I run my unit tests I get this error:
1) should make a request to the expected URL when running on localhost
test module getDataFromApi function
TypeError: Cannot read property 'fail' of undefined
In my unit test I've created a Jasmine Spy, which returns the done and fail methods. What am I doing wrong here?
Here is my unit test:
describe('getFundDataFromApi function', function () {
beforeEach(function () {
spyOn($, "getJSON").and.callFake(function () {
return {
done: function () {},
fail: function () {}
};
});
});
it('should make a request to the expected URL when running on localhost', function () {
var expectedUrl = '/assets/mock-data/mock-data.json';
module.getDataFromApi();
expect($.getJSON).toHaveBeenCalled();
expect($.getJSON).toHaveBeenCalledWith({url:expectedUrl});
});
});
Function I'm trying to test: getDataFromApi
getDataFromApi: function () {
var mod = this;
var url = this.settings.apiUrl;
$.getJSON({
url: url
})
.done(function (data) {
mod.processApiData(data);
})
.fail(function () {
mod.displayErrorMessage();
});
},
In your function getDataFromApi you are chaining the call of fail after done but, in the mocked version of done, it returns nothing(undefined), so, you get TypeError: Cannot read property 'fail' of undefined.
You can make the done function to return an object with a fail property that is a function.
beforeEach(function() {
spyOn($, "getJSON").and.callFake(function() {
return {
done: function() {
return { fail: function() {} };
}
};
});
});
Or, one line ES6 version
spyOn($, "getJSON").and.callFake(() => ({ done: () => ({fail: () => {}}) }));
Or, if you are planning to do more in your tests, like testing success or failed responses, probably returning a jQuery Deferred can help you
beforeEach(function() {
spyOn($, "getJSON").and.callFake(function() {
const deferred = $.Deferred();
deferred.resolve({'success': true});
return deferred;
});
});
Calling deferred.reject({'success': false}); will give you the chance to test for errors.
Hope it helps
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?
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);
});
});
I'm having a problem unit testing the following method:
$scope.changeLocation = function (url) {
$location.path(url).search({ ref: "outline" });
};
I've written the following unit test that currently fails with this error (TypeError: Cannot read property 'search' of undefined):
var $locationMock = { path: function () { }, search: function () { } };
it('changeLocation should update location correctly', function () {
$controllerConstructor('CourseOutlineCtrl', { $scope: $scope, $location: $locationMock });
var url = "/url/";
spyOn($locationMock, "path");
spyOn($locationMock, "search");
$scope.changeLocation(url);
expect($locationMock.search).toHaveBeenCalledWith({ ref: "outline" });
expect($locationMock.path).toHaveBeenCalledWith(url);
});
If I change my function to the following, the test passes:
$scope.changeLocation = function (url) {
$location.path(url);
$location.search({ ref: "outline" });
};
How do I unit test this method when I'm using method chaining? Do I need to setup my $locationMock differently? For the life of me I cannot figure this out.
That is because your mock does not return location object to be able to chain through. Using Jasmine 2.0 you can change your mock to:
var $locationMock = { path: function () { return $locationMock; },
search: function () { return $locationMock; } };
and
spyOn($locationMock, "path").and.callThrough();
spyOn($locationMock, "search").and.callThrough(); //if you are chaining from search
or add:
spyOn($locationMock, "path").and.returnValue($locationMock);
spyOn($locationMock, "search").and.returnValue($locationMock); //if you are chaining from search
Or just create a spy object (less code):
var $locationMock = jasmine.createSpyObj('locationMock', ['path', 'search']);
and
$locationMock.path.and.returnValue($locationMock);
$locationMock.search.and.returnValue($locationMock); //if you are chaining from search
try :
spyOn($locationMock, "path").and.callThrough();
Else you'r calling search on a mock not $location
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');
});
});