Test a function which uses document properties in qunit - javascript

I have a function like this which I would like to write a unit test for :
function A () {
var x = document.referrer;
//Do something with 'a'
}
I would like to write a unit test, to test this function. I'm using QUnit with sinon.js for stubs and mocks. Is there a way for me to mock the document object so that I can provide a value for the referrer and test my logic? I tried assigning a value to document.referrer in my setup function, but that didn't work
Update
This is the test which I'm trying :
module("Verify Referrer", {
setup : function () {
this.documentMock = sinon.mock(document);
this.documentMock.referrer = "http://www.test.com";
},
teardown : function () {
this.documentMock.restore();
}
});
test("UrlFilter test url in white list", function() {
equal(document.referrer, "http://www.test.com");
});
I also tried using stub

Related

Angular Service becomes undefined when spyOn it

I have an Angular 1.6.6 application which I'm testing with Karma and Jasmine.
Given this code from controller:
$scope.undo = function () {
return $scope.isUndoDisabled() || $scope.undoAction();
};
$scope.isUndoDisabled = function () {
return HistoryService.isUndoDisabled();
};
I have been testing it with the following spec:
it('undoAction should not be called with default settings', function () {
var $scope = {};
var controller = $controller('PaintController', { $scope: $scope });
spyOn($scope, 'undoAction');
//spyOn(HistoryService, 'isUndoDisabled');
$scope.undo();
expect($scope.undoAction).not.toHaveBeenCalled();
});
And is passing the test, but when I uncomment the spyOn of HistoryService, the call HistoryService.isUndoDisabled() from $scope.isUndoDisabled returns undefined and then the test fails because:
Expected spy undoAction not to have been called.
Any idea about what's going on???? It seems like the spyOn is affecting to the code??
spyOn(...) is a shortcut for spyOn(...).and.stub(), not spyOn(...).and.callThrough(). When being spied this way, HistoryService.isUndoDisabled() returns undefined.
The proper way to test the unit is to isolate it from others. Since it is the controller that is tested, the service should be mocked or stubbed:
spyOn(HistoryService, 'isUndoDisabled').and.returnValue(true);
And then in another test:
spyOn(HistoryService, 'isUndoDisabled').and.returnValue(false);
I think if you want to call isUndoDisabled() from HistoryService, the function $scope.isUndoDisabled should be
$scope.isUndoDisabled = function () {
HistoryService.isUndoDisabled();
};
There shouldn't be a return in the body

Using external class during client side Mocha unit testing

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'
}

First test with mocha

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))

Qunit: Test leakage

I have a leakage problem when testing my jQuery plugin. The problem occurs when I want to mock out a value or function on a literal object.
Example:
test('Overwrite some default setting', function(){
$.fn.plugin.defaults.bar = 'foo';
});
test('Bar should be undefined', function(){
equals( $.fn.plugin.defaults.bar, undefined );
});
This test will fail because the first test added the 'bar' var to defaults. I fixed it with the following code but taking a copy of a copy doesn't look very elegant.
$(function(){
/*
* Trolley Button Base: Options.
*/
var defaults_copy = $.extend({}, $.fn.plugin.defaults );
var setdown = {
setup : function(){
$.fn.plugin.defaults = $.extend({}, defaults_copy);
},
teardown : function(){ }
};
module('Test leakage', setdown );
test('Overwrite some default setting', function(){
$.fn.plugin.defaults.bar = 'foo';
});
test('Bar should be undefined', function(){
equals( $.fn.plugin.defaults.bar, undefined );
});
})
Also if I have a few objects in the jQuery namespace it might become a little messy if I have to take multiple copies of each object. So was wondering does anybody have a better solution to 'reset' all objects?
This is by design for QUnit. At the end of each test, you should clean up any state changes you've made. I don't know of a way to automatically do that -- you have to write the code to undo the effects of any test code you've written, like this:
test('Overwrite some default setting', function(){
// test code
$.fn.plugin.defaults.bar = 'foo';
// cleanup code
delete $.fn.plugin.defaults.bar;
});
test('Bar should be undefined', function(){
equals( $.fn.plugin.defaults.bar, undefined );
});
To prevent issues with test order dependency and to isolate your unit tests fully, you will need to manually implement Test level setup and tear down functionality by creating a function for each and including it at the beginning and end of each of your test methods:
e.g.
$(document).ready(function () {
// Test Setup/TearDown
function codeUnderTestModuleTestSetup() {
//any setup needed
}
function resetDefaults() {
//code in here to reset defaults
}
function resetSomethingElse() {
//code in here to reset something else
}
function codeUnderTestModuleTestTearDown() {
resetDefaults();
resetSomethingElse();
}
//Tests
module('Your module title Test Harness');
test('FunctionUnderTest_Behaviour_ExpectedResult', 1, function () {
codeUnderTestModuleTestSetup();
//Arrange
//code removed
//Act
//Code removed
//Assert
//Code removed
codeUnderTestModuleTestTearDown();
});
}
You can manually implement module level and test run level setup and teardown functions too, if necessary.

Is there anyway to unit test javascript functions defined within a function?

I would just like to ask whether I would be able to unit test the code inside ExternalFunction within the document.ready? I have tried many things for a while now and still couldn't work out how, and am at my wits end.
$(document).ready(function () {
var originalExternalFunction = ExternalFunction;
ExternalFunction = function(context, param) {
// trying to unit test the stuff in here!
}
}
I'm unit testing using JsTestDriver. Test declaration is something like TestThisTest.prototype.test_this - function() {};
Thanks in advance.
Since, in your example, ExternalFunction is not declared within the scope of the function, it is global (or at least, in whatever scope it may have been defined in outside ready). You can therefore test it by calling it as a global.
The trouble is, in order to assign the function to ExternalFunction, you have to run ready (which you could run manually, if you need). This means that if you put any other functionality in ready, then no, it is not unit testable. If your example code is an accurate reflection of reality, then I suppose it is kinda testable.
The point of a construct like this, is to hide the inner function. If you don't wish to hide it, then Anon.'s suggestion of defining newExternalFunction in a more accessible scope is what you need.
If your function needs to be a closure using variables from within ready, you could define newExternalFunction thus:
var newExternalFunction;
$(document).ready(function () {
var originalExternalFunction = ExternalFunction;
newExternalFunction = function(context, param) {
// trying to unit test the stuff in here!
}
ExternalFunction = newExternalFunction;
}
You would still need to ensure that ready has run, prior to unit testing, but you wouldn't have to rely on ExternalFunction not being reset to originalExternalFunction.
You could do something like:
function newExternalFunction(context, param) {
//etc.
}
$(document).ready(function () {
var originalExternalFunction = ExternalFunction;
ExternalFunction = newExternalFunction;
}
Then it's relatively straightforward to run your unit tests on newExternalFunction.
Theoretically, you could do something like:
ExternalFunction = function() { }
ExecuteDocumentReady(); // implement a mock on $(document).ready(fn) to store the function, and then execute it here
ExternalFunction(fakeContext, fakeParam);
assert(fakeContext.foo == 12); // or whatever you need it to do
That being said, I'm not sure exactly how to do that in javascript.
You could use a closure to generate your callback function:
// create function to make your "extension" function
function createHookFunction(callback) {
// return a function
return function(context, param) {
var ret;
// // trying to unit test the stuff in here!
if (typeof callback == 'function') {
// if you want to trap the return value from callback,
// ret = callback.apply(...);
callback.apply(this, arguments);
}
return ret;
};
}
// your hook now becomes:
$(document).ready(function() {
ExternalFunction = createHookFunction(ExternalFunction);
});
// and your unit test becomes:
var funcToTest = createHookFunction();
funcToTest(testContext, testParam);
// And, you could even test that the callback itself gets called
function someTest() {
var testContext = {}, testParam='test';
var callbackCalled = false;
var funcToTest = createHookFunction(function(context, param) {
callbackCalled = (context === testContext) && (param === testParam);
});
return (funcToTest(testContext, testParam) == 'Expected Return') && callbackCalled;
}

Categories

Resources