I am writing a javascript library that calls a method on another js lib.
Most of the time i would create a mock function of the 3rd party library and spy on it. However, it doesn't seem to work.
For example:
mymain.js
export const checkForExternalFunc = () => {
try {
return com.externalFunc
} catch (error) {
return false
}
}
mymain_spec.js
import { checkForExternalFunc } from './src';
describe('checkForExternalFunc', () => {
let com = com || {};
com.externalFunc = function () {
return true;
};
it('return the function when com.externalFunc is present', () => {
spyOn(com, "externalFunc");
let check = checkForExternalFunc();
expect(check).toBe(jasmine.Any(function));
});
})
and this would give me an error
ReferenceError: com is not defined
Function in 3rd part library
var com = com || {};
com.externalFunc = function () {
// return something
};
Any suggestion how i can approach this? Also i have researched a little on Stub with Sinon but not sure how to use it properly. Any help will be appreciated. Thanks!!
Note: I setup project with webpack + babel, karma, jasmine.
Thanks #AdityaBhave for pointing out. I only need to make sure my mock function and the actual one are actually the same. Please see comment above.
Related
I'm struggling with using spyOn as part of testing my utils.js module. I've tried various methods and approaches but all seem to yield the "expected mock function to have been called". For the record, other unit tests work OK, so there shouldn't be any issue with my actual test setup.
Below is a simplified test case with two functions and one test, and I can't even get these to work. Did I misunderstand the spyOn altogether?
// utils.js
function capitalHelper(string){
return string.toUpperCase();
}
function getCapitalName(inputString){
return capitalHelper(inputString.charAt(0)) + inputString.slice(1);
}
exports.capitalHelper = capitalHelper
exports.getCapitalName = getCapitalName
// utils.test.js
const Utils = require('./utils');
test('helper function was called', () => {
const capitalHelperSpy = jest.spyOn(Utils, 'capitalHelper');
const newString = Utils.getCapitalName('john');
expect(Utils.capitalHelper).toHaveBeenCalled();
})
I do ont use spyOn(), but jest.fn() instead for all mock scenario
In your case I would do the following
test('helper function was called', () => {
Utils.capitalHelper = jest.fn((s) => Utils.capitalHelper(s))
const newString = Utils.getCapitalName('john')
expect(Utils.capitalHelper.mock.calls.length).toBe(1)
})
First line could have simply be :
Utils.capitalHelper = jest.fn()
since you don't seem to be testing the returned value in your test :)
You can find more details on jest.fn() on the jest official documentation : https://facebook.github.io/jest/docs/en/mock-functions.html
----------------------- EDIT
I got it : the problem occurs because within your utils.js file, getCapitalName uses the defined function, not the one pointed by the export.
To be able to mock the function in use you could change your utils.js file to
// utils.js
const Utils = {
capitalHelper: string => string.toUpperCase(),
getCapitalName: inputString => Utils.capitalHelper(inputString.charAt(0)) + inputString.slice(1)
}
export default Utils
then the tests I gave before will work
While the official docs says,
While window is globally available in JavaScript, it causes testability problems, because it is a global variable. In AngularJS we always refer to it through the $window service, so it may be overridden, removed or mocked for testing.
I still cannot make sense of it. 😕 How can I benefit from $window in my unit tests code? In my snippet below, I can spy/mock and make use of the native window object, with or without $window. How does it cause testability problems?
angular.module('messagePopper', [])
.factory('popper', function popperFactory($window) {
return {
popupMessage(message) {
alert(message);
},
popupMessageWith$window(message) {
$window.alert(message);
}
};
});
describe('messagePopper: popper service', () => {
let $injector;
let $window;
let popper;
beforeEach(() => {
module('messagePopper');
inject((_$injector_) => {
$injector = _$injector_;
$window = $injector.get('$window');
popper = $injector.get('popper');
});
});
it('should popupMessage correctly', () => {
const message = 'welcome glenn#foodie.net';
const alertMock = spyOn(window, 'alert');
popper.popupMessage(message);
expect(alertMock)
.toHaveBeenCalledWith(message);
});
it('should popupMessageWith$window correctly', () => {
const message = 'welcome glenn#foodie.net';
const alertMock = spyOn($window, 'alert');
popper.popupMessageWith$window(message);
expect(alertMock)
.toHaveBeenCalledWith(message);
});
});
Fiddle here: https://jsfiddle.net/glenn/x42uex66.
While using the global window object works, there might be times where a test will fail, causing the windows object to not be cleaned up for the next test. In which case you would suddenly see a lot of tests failing for seemingly no reason, instead of just the one test that caused the issue.
I am running unit tests on a javascript class using Mocha using the follow methodology, firstly the test:
var base = require('../moduleone.js');
describe("some test", function() {
it("description", function() {
var check = base.toBeTested(dummyValue)
//test is here ...
});
});
the moduleone.js containing function to be tested:
function toBeTested(category){
//below I calling an assert function defined in moduletwo
//works fine when running in browser
assert(type(category)=='string','category is string type');
//more code..
return something
module.exports.toBeTested = toBeTested;
moduletwo.js:
function assert(outcome, description) {
//see code.tutsplus.com quick and easy javascript testing with assert
var li = outcome ? 'pass' : 'fail';
if (li == 'fail') {
console.log('FAIL: '+description);
}
else {
console.log('PASS: '+description);
}
}
The issue I have is mocha doesn't know anything about moduletwo and when the moduleone function calles the function in moduletwo mocha throws a ReferenceError: assert is not defined. How can I link all my dependencies so that mocha can see them?
In your moduleone.js be sure that you are requireing moduletwo.js to bring your assert function into scope for moduleone.js. Otherwise, you get a ReferenceError, not for any reasons with mocha, but because moduleone does not have access to assert.
// moduletwo.js
function assert(outcome, description) { /* your functionality */ }
module.exports = assert
// moduleone.js
var assert = require('./moduletwo')
function toBeTested(category) { /* your functionality that uses assert */ }
module.exports.toBeTested = toBeTested
Now, with regards to that tutorial. If you are following it to learn how to make an easy assert module, that is fine. But if you are trying to test some code that does something else, consider using an existing assertion library like chai. For example:
// example.test.js
var expect = require('chai').expect
var isValidCategory = require('./is-valid-category')
describe('isValidCategory(category)', function () {
it('validates string categories', function () {
expect(isValidCategory('A String Category')).to.be.true
})
it('does not validate non-string categories', function () {
expect(isValidCategory(['an', 'array', 'somehow'])).to.be.false
})
})
// is-valid-category.js
module.exports = function isValidCategory(category) {
return typeof category === 'string'
}
I mactually trying to run my first unit test with mocha using this code :
var assert = require('assert');
var returnCool = function () {
return 1;
}
describe("Array contains", function () {
it('should return-1 when the value is not present', function () {
returnCool().should.equal(1);
});
});
The problem is that my code is actually failing everytime.
I tried with the sample in mocha website :
describe('Array', function(){
describe('#indexOf()', function(){
it('should return -1 when the value is not present', function(){
[1,2,3].indexOf(5).should.equal(-1);
[1,2,3].indexOf(0).should.equal(-1);
})
})
})
And it fails too.
What am I doing wrong ?
Thanks for advance
Looks like you are not calling your assertion library. You are currently calling .should() on an integer
You have included an assert library but are using should - style asserts. Either include should.js or use assert-style asserts (assert.equal([1,2,3].indexOf(5), -1))
I'm not sure if I'm writing the title correct, but here's what I want to do.
I have this code
var callback = function(result) {
if(result.count < 5) {
msg_id = result.msg_id;
MovieService.getMovies(msg_id, result.count).get(callback, error);
}
if(result.movies.length !== 0) {
setDataToDisplay(result);
}
if(result.count === 5) {
$scope.loading = false;
}
}
MovieService.getMovies(msg_id, 0).get(callback, error);
Basically, when user comes in the first time MovieService will be called and it gets called until the count equals to 5 times. It's like a recursive loop. Now if I want to test this code, I don't know how to do chained stub in Jasmine. I could do something similar in Mockito.
Here's my test so far.
it("should give me the lot of movies", function() {
var movie1 = new MovieBuilder().withTweetId('8').build();
var movie2 = new MovieBuilder().withId('3812').withTweetId('8').build();
var movie3 = new MovieBuilder().withId('3813').withTweetId('8').build();
var movie4 = new MovieBuilder().withId('3814').withTweetId('8').build();
movieService = {
getMovies : function() {
return {
get : function(callback, error) {
callback(
{
'msg_id' : '8',
'count' : '5',
'movies' : [movie1, movie2, movie3, movie4]
});
}
}
}
}
ctrl = controller('MovieTwitterCtrl', {$scope : scope, MovieService : movieService});
expect(scope.movie_groups[0].length).toBe(4);
expect(scope.msg_id).toBe('8');
});
But if I want to test the second, third, fourth and fifth call. How do I do that? Does Jasmine offer something like Mockito? Or how do I do that in pure javascript?
Thanks a lot.
You might want to take a look at Sinon, which is a library that provides methods for spys, stubs and mocks and is compatible with Jasmine.
In order to automatically invoke your callbacks, you would use stub.yields() or stub.yieldsTo(). You've also got spy.getCall(n) that will let you verify the way your method was called during the nth time. Sinon is written in a way that stubs are also spies... so if you create a stub for your movieService, you'll have access to both yields() and getCall(n).