Qunit test not recognizing defined object - qunit

I have a JS method I'm looking to test:
doMediation: function() {
var state,
that = this,
mediation_rule = "asdf";
Mediation.getRules(mediation_rule, function () {
[blah blah]
});
},
My Mediation object is defined above this method in my class. This is my qunit test:
test('doMediation: testing mediation', 1, function(){
var proto = $.extend({}, My.prototype, {
Mediation: {
getRules: function(rule, cb){}
}
proto.doMediation();
});
The problem is that I'm getting an error:
Died on test #1: Cannot call method 'getRules' of undefined - {}
Yet, it is defined right there in the test. Thanks.

Turns out that previously, Mediation was defined as such: var Mediation = AnotherClass.Mediation. In the qunit test, I had to actually define AnotherClass.Mediation = {} instead of Mediation{}

Related

.then is not a function Angularjs factory

I just started learning Jasmine test cases for angularjs. I am unable to test below code.Kindly help
$scope.getConstants = function(lovName) {
ConstantService.getConstants(lovName).then(function(d) {
switch (lovName) {
case 'WORKFLOW':
$scope.workflowTypes = d;
$scope.loadCounterpartyTmp();
break;
--------Other Cases
}
My ConstantService is defined as
App.factory('ConstantService', [ '$http', '$q', function($http, $q) {
return {
getConstants : function(lovName) {
return $http.post('/sdwt/data/getConstants/', lovName).then(function(response) {
return response.data;
}, function(errResponse) {
return $q.reject(errResponse);
});
}
I want to test getConstants function.I need to create a mock of ConstantService and pass the data to it.
I have written below test case but the test case is not working.Please let me know how to test the above code
describe('getConstantsForMurexEntity', function() {
it('testing getConstantsForMurexEntity function', function() {
var d=[];
d.push(
{id:1,value:'ABC'},
{id:2,value:'DEF'},
{id:3,value:'IJK'},
{id:4,value:'XYZ'},
);
//defined controller
spyOn(ConstantService, 'getConstants').and.returnValue(d);
$scope.getConstants('WORKFLOW');
expect($scope.workflowTypes).toBe(d);
The above test case is not working as it is saying "ConstantService.getConstants(...).then is not a function".
Your ConstantService.getConstants() function returns a promise, which your actual code is using, with the .then() call. This means means that when you spy on it, you also need to return a promise, which you are not doing. Because you are not returning a promise, when your actual call tries to call .then(), it is undefined, which is the reason for the error message.
Also, you aren't using Array.push correctly.
Your test should probably look something like the following (note, this is untested):
describe('getConstantsForMurexEntity', function() {
it('should set workflowTypes to the resolved value when lovName is "WORKFLOW"', inject(function($q) {
var deferred = $q.defer();
spyOn(ConstantService, 'getConstants').and.returnValue(deferred.promise);
var d = [
{id:1,value:'ABC'},
{id:2,value:'DEF'},
{id:3,value:'IJK'},
{id:4,value:'XYZ'},
];
$scope.getConstants('WORKFLOW');
deferred.resolve(d);
$scope.$apply();
expect($scope.workflowTypes).toBe(d);
}));
});

How to avoid jasmine error if testing the property of an undefined object

With jasmine, when I try to test the property of an object :
var foo = {property1:'123'}
it('foo.property1 should be 123'', function() {
expect(foo.property1).toEqual("123");
});
it('bar.property2 should be 456', function() {
expect(bar.property2).toEqual("456");
});
If this object is undefined, I get the error :
TypeError: Cannot read property 'property2' of undefined
and it breaks ALL my spec.
To solve the problem I use a conditional to test if bar exists in the first place :
var foo = {property1:'123'}
it('foo.property1 should be 123'', function() {
expect(foo.property1).toEqual("123");
});
it('bar.property2 should be 456', function() {
if(!bar){
fail("bar does not exist");
}else{
expect(bar.property2).toEqual("456");
}
});
Is there a better and more elegant way ?

Using Jasmine spyOn to mock function in Browserify module

I'm trying to run a unit test on a function (testFunc). testFunc calls another function (secondFunc) which I would like to mock. Can I mock secondFunc so that when it is called in the context of testFunc, the spiedOn version of secondFunc is called? If not, how should I reformat my browserify module to make it testable?
Currently the setup looks something like this:
app.js (Browserify Module)
module.exports = (function () {
function testFunc() {
secondFunc();
}
function secondFunc(){
console.log('not mocked!');
}
return {
testFunc, secondFunc
};
})();
test.js (Jasmine Test)
describe("testFunc", () => {
let app = require('./app');
beforeEach(() => {
spyOn(app, 'secondFunc');
});
it("should call secondFunc spy", () => {
app.testFunc();
expect(app.secondFunc).toHaveBeenCalled();
});
});
The way you have it now, the spyOn is replacing the secondFunc property on your returned object with a proxy, but your code calls the secondFunc function that is inside the closure of the anonymous function. There are several ways to restructure your code to better expose the functions.
You could structure your module this way:
exports.testFunc = function() {
exports.secondFunc();
}
exports.secondFunc = function(){
console.log('not mocked!');
}
which is a lot smaller, easier to read, and let you mock the secondFunc function.
The reason this is happening is because you are setting up a mock on the returned object, but the code is calling the internal function. What I've done in the past is something like this:
module.exports = (function () {
function testFunc() {
api.secondFunc(); // Call the API function, which is what is mocked
}
function secondFunc(){
console.log('not mocked!');
}
var api = {
testFunc, secondFunc
};
return api;
})();

Async call in beforeAll

Here are 2 samples of the same test. The only difference is that first one uses a promise in beforeAll block to assign a value to the variable while the second one assigns the value directly.
I raised a similar question Running spec after promise has been resolved with one of the comments pointing to this issue https://github.com/jasmine/jasmine/issues/412 which says that this is not supported in Jasmine. Has somebody figured out any workaround?
This fails with TypeError: Cannot read property 'forEach' of undefined
describe('Async car test', function () {
var cars;
beforeAll(function (done) {
// getCars() is a promise which resolves to ['audi', 'bmw']
getCars().then(function (data) {
cars = data;
console.log(cars) // ['audi', 'bmw']
done();
});
});
cars.forEach(function (car) {
it('car ' + car, function () {
expect(car).toBe(car);
});
});
});
This works fine
describe('Car test', function () {
var cars = ['audi', 'bmw'];
cars.forEach(function (car) {
it('car ' + car, function () {
expect(car).toBe(car);
});
});
});
Posting it as an answer, because I can't see things properly in comments.
I'm actually generating tests in my spec as well, and I'm using https://www.npmjs.com/package/jasmine-data-provider , I think you probably cannot generate it directly from resolved promise. And wrapping in another it doesn't work for you. This should work:
var using = require('jasmine-data-provider');
using(cars.forEach, function (car) {
it(car + ' should be' + car, function () {
expect(car).toBe(car);
});
});
This is not an issue with jasmine, it is an issue with your code.
beforeAll does not block subsequent code below the statement. it blocks code that is defined in it('should ...', (done)=>{...});
it('should have cars', (done) => {
cars.forEach(function (car) {
expect(car).toBe(car);
});
});
Since Jasmine does not support adding tests at runtime, the trick is to request the asynchronous data before starting Jasmine, and then using the retrieved data during runtime instead. This can be achieved with a singleton and programmatically starting Jasmine.
See here for a working example.
// car-collection.js
class CarCollection {
static load() {
return this.request()
then((data) => this.cars = data);
}
static request() {
// in practice this function would do something cooler
return Promise.resolve(['audi', 'bmw']);
}
}
modules.export = CarCollection;
Since CarCollection has methods that are static they will be shared across imports and this.cars will persist.
// launcher.js
const Jasmine = require('jasmine');
const CarCollection = require('./car-collection');
CarCollection.load()
.then(() => {
console.log(`car count is ${CarCollection.cars.length}`); // prints: car count is 2
const jasmine = new Jasmine();
jasmine.loadConfigFile(...); // path to jasmine.json
jasmine.execute();
});
An important step here is configure jasmine to know where to look for the test files. Either by loading a config or passing specifics into the execute function.
// car.spec.js
const CarCollection = require('./car-collection');
describe('test', function () {
CarCollection.cars.forEach((car) => {
it('test' + car, () => {
expect(car).toBe(car);
});
});
});
Now run node ./launcher.js and the tests should run.

How to verify with Jasmine that a module called a sub-module method with correct arguments

The following test spec simulates calling a module that writes some content to the file system. Internally it uses fs to do that. I want to make sure fs.writeFile() was called with correct parameters. However, the toHaveBeenCalledWith() doesn't seem to work with a generic Function argument. Any ideas on how to make toHaveBeenCalledWith work as I expect?
Test Spec:
var fs = {
writeFile: function (arg1, arg2, cb){}
}
var writeContent = {
toFS: function (){
var path = "some/calculated/path";
var content = "some content";
fs.writeFile(path, content, function(){})
}
}
describe("writeContent", function() {
var writeFileSpy = null;
beforeEach(function() {
writeFileSpy = jasmine.createSpy('writeFileSpy');
spyOn(fs, 'writeFile').and.callFake(writeFileSpy);
});
it("can call spy with callback", function() {
writeContent.toFS();
expect(writeFileSpy).toHaveBeenCalledWith("some/calculated/path", "some content", Function);
});
});
Results:
Message:
Expected spy writeFileSpy to have been called with [ 'some/calculated/path', 'some content', Function ] but actual calls were [ 'some/calculated/path', 'some content', Function ].
Answering my own question :-) Just needed to enclose Function in jasmine.any() as in:
expect(writeFileSpy).toHaveBeenCalledWith("some/calculated/path", "some content", jasmine.any(Function));

Resources