I have the following (simplified) javascript module which uses jQuery Cookie plugin to check if cookies are enabled. If cookies are disabled it warns the user:
var cookiePolicy = (function () {
var cookiesEnabled = function () {
return $.cookie('check', 'valid', { expires: 1 }) && $.cookie('check') == 'valid';
};
return {
updateCookiePolicy: function () {
if (!cookiesEnabled()) {
$("#cookie-policy").append('<p id="cookie-warning">Cookies are disabled. Some features of this site may not work correctly.</p>');
}
}
};
})();
I have the following unit test:
QUnit.test("When cookies are enabled the cookie policy text remains unchanged", function (assert) {
sinon.mock($).expects("cookie").once().withExactArgs("check", "valid", { expires: 1 });
sinon.mock($).expects("cookie").once().withExactArgs("check").returns("valid");
cookiePolicy.updateCookiePolicy();
assert.equal(0, $('#cookie-warning').length, "Failed!");
});
The test fails because "cookie is already wrapped". I assume this is because I am mocking $.cookie for both set and read. How can I mock the call to $.cookie for both setting and reading in this test?
Your assumption is correct. Depending on the version of Sinon you're using, you could do something like this:
// UUT
var foo = {
bar: function() {}
};
// Test setup
var mock = sinon.mock(foo);
var expectation = mock.expects('bar').twice();
expectation.onFirstCall().stub.calledWithExactly('baz');
expectation.onSecondCall().stub.calledWithExactly('qux');
// Test
foo.bar('baz');
foo.bar('qux');
mock.verify();
BTW, it's strange to use Sinon mocks without using .verify(). Maybe stubs would be a better fit?
Related
I have the following function which checks if cookies are enabled in a user's browser:
CookiesEnabled: function() {
var cookieEnabled = navigator.cookieEnabled;
if (!cookieEnabled) {
document.cookie = "test_cookie";
cookieEnabled = document.cookie.indexOf("test_cookie") != -1;
}
return cookieEnabled;
},
I want to unit test that this works using Sinon/Qunit but am unsure of how to approach this properly as I am not very experienced using Sinon or QUnit. Here is my attempt at putting together a test:
QUnit.test("CookiesEnabled - returns true if user has enabled cookies in their browser", function (assert) {
sinon.stub(CookieHelper, "CookiesEnabled")
.callsFake(function () {});
var result = CookieHelper.CookiesEnabled();
Assert.Equal(result, true);
CookieHelper.CookiesEnabled.restore();
});
Please can anyone point me in the right direction for unit testing this method using Qunit and Sinon? Thank you.
UPDATED ATTEMPT:
QUnit.test("CookiesEnabled - returns true if user has enabled cookies in their browser", function (assert) {
sinon.stub(navigator, "cookieEnabled").callsFake(function () {
return true;
});
var result = CookieHelper.CookiesEnabled();
assert.equal(result, true);
});
As mentioned, window.navigator is readonly. There are some things you could try to fake out the browser, but really, you should just inject the properties you're interested in for the function to use. This way you can override the browser setting or test it! Notice the addition of two arguments (since you also need to bypass your double-checking of cookie setting).
The additional flag argument is a boolean that essentially tells the function what the value of cookieEnabled should be (versus using navigator.cookieEnabled). However, if that parameter is missing, the code will use the browser setting. In other words, it's a way to "inject" the value you want, very useful in testing. This isn't ideal, but not much else you can without a lot more work and mocking (using a headless browser with cookies disabled).
Here's a jsfiddle of this working: https://jsfiddle.net/e5mtpboy/
CookiesEnabled: function(flag, bypassTest) {
var cookieEnabled = typeof(flag) === 'boolean' ? flag : navigator.cookieEnabled;
if (!cookieEnabled && !bypassTest) {
document.cookie = "test_cookie";
cookieEnabled = document.cookie.indexOf("test_cookie") != -1;
}
return cookieEnabled;
}
Then in your tests:
QUnit.test("CookiesEnabled - returns true if user has enabled cookies in their browser", function (assert) {
var result = CookieHelper.CookiesEnabled(true);
assert.equal(result, true);
});
QUnit.test("CookiesEnabled - returns true if user has disabled cookies, but we can still set one", function (assert) {
var result = CookieHelper.CookiesEnabled(false);
assert.equal(result, true);
});
QUnit.test("CookiesEnabled - returns false if browser does not support cookies", function (assert) {
var result = CookieHelper.CookiesEnabled(false, true);
assert.equal(result, false);
});
I'm building a command-line application in NodeJS and I want to thoroughly test it using Jasmine.
I've implemented a promptUser() method which uses Node's readline.createInterface method to pose a question and pipe the response into a callback. I want to test that, given a user response of 'q', my module's quit() function is called.
However I'm struggling to test this. I don't really want to test the readline method directly, since I didn't write that code, but I reasoned that if I can create a listener on process.stdout.write then when enter command: is printed to the screen I can respond with process.stdin.write("q\n") and trigger the if/else logic.
I've simplified the code, but should explain what I'm trying to do:
Module source code:
var Cli = function() {
var rl = require('readline');
var self = this;
Cli.prototype.promptUser = function() {
var inputHandler = rl.createInterface(process.stdin, process.stdout);
inputHandler.question('enter command: ', function(answer) {
if (answer === 'q') {
self.quit();
};
});
};
Cli.prototype.quit = function() {
// doSomething
};
};
module.exports = Cli;
Jasmine test:
var Cli = require('Cli');
describe('My application.', function() {
beforeEach(function() {
cli = new Cli();
spyOn(cli, 'quit');
});
describe('Cli #promptUser', function() {
it('input of lower-case q calls cli.quit()', function() {
process.stdout.once('write', function() {
process.stdin.write("q\n");
});
cli.promptUser();
expect(cli.quit).toHaveBeenCalled();
});
});
});
I'm looking to either make this approach work or find a better way to test my code. I suspect there is probably a superior/more direct approach.
I'm trying to unit test a situation with postMessage and addEventListener. The use case is that I use a separate window for user logins similar to the OAuth workflow and then use postMessage in the login window to notify the main window that the user has logged in. This is the listening code that would be in the main window:
$window.addEventListener("message", function(event) {
if (event.data.type === "authLogin") {
service.curUser = event.data.user;
utilities.safeApply($rootScope);
$window.postMessage({type: "authLoginSuccess"}, '*');
}
});
The utilities.safeApply function is defined as:
// Run $apply() if not already in digest phase.
utilitiesService.safeApply = function(scope, fn) {
return (scope.$$phase || scope.$root.$$phase) ? scope.$eval(fn) : scope.$apply(fn);
};
My unit test is designed to send a postMessage to simulate the login:
describe('auth login postMessage', function() {
var testUser = {handle: 'test'};
beforeEach(function(done) {
$window.postMessage({type: 'authLogin', user: testUser}, '*');
function onAuthLoginSuccess(event) {
$window.removeEventListener('message', onAuthLoginSuccess);
done();
}
$window.addEventListener("message", onAuthLoginSuccess);
});
it("should set the user object", function() {
expect(service.curUser).toEqual(testUser);
});
});
This is the result of running the unit test:
12 09 2015 14:10:02.952:INFO [launcher]: Starting browser Chrome
12 09 2015 14:10:05.527:INFO [Chrome 45.0.2454 (Mac OS X 10.10.5)]: Connected on socket 537CxfI4xPnR0yjLAAAA with id 12583721
Chrome 45.0.2454 (Mac OS X 10.10.5) ERROR
Uncaught Error: Unexpected request: GET security/loginModal.tpl.html
No more request expected
at http://localhost:8089/__test/Users/userX/esupport/code/proj/public/vendor/angular-mocks/angular-mocks.js:250
Chrome 45.0.2454 (Mac OS X 10.10.5): Executed 23 of 100 (skipped 60) ERROR (0.333 secs / 0.263 secs)
I'm at a loss of why it would try to load an HTML template. I've narrowed it down so that if I don't call the $scope.$apply() function, the unit test succeeds without error. However, I need that $apply() to update the view.
I've tried stubbing out the utilities.safeApply method in the unit test and also tried to set up an expectation for the HTML template GET request. These attempts look like:
describe('auth login postMessage', function() {
var testUser = {handle: 'test'};
beforeEach(function(done) {
$httpBackend.when('GET', 'security/loginModal.tpl.html').respond(''); // <-- NEW
spyOn(utilities, 'safeApply').and.callFake(angular.noop); // <-- NEW
window.postMessage({type: 'authLogin', user: testUser}, '*');
function onAuthLoginSuccess(event) {
window.removeEventListener('message', onAuthLoginSuccess);
done();
}
window.addEventListener("message", onAuthLoginSuccess);
});
it("should set the user object", function() {
expect(service.curUser).toEqual(testUser);
});
});
Both of these attempts do nothing. I still get the same error message. I've tried other debugging steps such as using spyOn for $location.path() so it returns a sample value like "/fake". In all other unit tests where I'm directly testing service methods and not using postMessage to trigger the service code, the stubbing works fine. However, in the addEventListener function, the $location.path() returns "" which points towards a theory that the addEventListener function is running in an entirely different instance than what the unit test has prepared. That would explain why the stubbed out functions aren't being used and why this other instance is trying to load a bogus template. This discussion also solidifies the theory.
So now the question is, how do I get it to work? i.e, how do I get the addEventListener function to run in the same instance where my stubbed functions are used and it doesn't make a request to the HTML template?
I'd just mock all the external parts and make sure the end result is what you expect.
Looking at your gist, you may need to mock some more services but this should be enough to test the "authLogin" message event.
describe('some test', function() {
var $window, utilities, toaster, securityRetryQueue, service, listeners;
beforeEach(function() {
module('security.service', function($provide) {
$provide.value('$window',
$window = jasmine.createSpyObj('$window', ['addEventListener', 'postMessage']));
$provide.value('utilities',
utilities = jasmine.createSpyObj('utilities', ['safeApply']));
$provide.value('toaster',
toaster = jasmine.createSpyObj('toaster', ['pop']));
$provide.value('securityRetryQueue',
securityRetryQueue = jasmine.createSpyObj('securityRetryQueue', ['hasMore', 'retryReason']));
// make sure you're not fetching actual data in a unit test
securityRetryQueue.onItemAddedCallbacks = [];
securityRetryQueue.hasMore.and.returnValue(false);
$window.addEventListener.and.callFake(function(event, listener) {
listeners[event] = listener;
});
});
inject(function(security) {
service = security;
});
});
it('registers a "message" event listener', function() {
expect($window.addEventListener).toHaveBeenCalledWith('message', listeners.message, false);
});
it('message event listener does stuff', inject(function($rootScope) {
var event = {
data: {
type: 'authLogin',
user: 'user'
},
stopPropagation: jasmine.createSpy('event.stopPropagation')
};
listeners.message(event);
expect(service.curUser).toBe(event.data.user);
expect(toaster.pop).toHaveBeenCalledWith('success', 'Successfully logged in.');
expect(utilities.safeApply).toHaveBeenCalledWith($rootScope);
expect($window.postMessage).toHaveBeenCalledWith({type: "authLoginSuccess"}, '*');
expect(event.stopPropagation).toHaveBeenCalled();
}));
});
I try to refactor my code. I know that if I have several expectations they should be isolate in 'it'. I try to understand how I can write instead this:
describe('my scenario should make', function () {
var config = browser.params;
var url = config.listOfReferencesUrl,
grid,
numberField;
it('test1', function () {
browser.get(url);
browser.executeScript("icms.go('WEB_INQ_PROC', 'InquiryList', null, 0)");
grid = psGrid(by.css("table[class='n-grid']"));
numberField = grid.getQuickFilter(1);
numberField.click().sendKeys("Hello!");
since('fail1').expect(numberField.getInputText()).toEqual("");
});
it('test2', function () {
since('fail2').expect(numberField.getInputText()).toEqual("Hello!");
});
});
Something like this:
describe('my scenario should make', function () {
var config = browser.params;
var url = config.listOfReferencesUrl,
grid,
numberField;
*********Make this part of code ONES before all tests in spec ****
browser.get(url);
browser.executeScript("icms.go('WEB_INQ_PROC', 'InquiryList', null, 0)");
grid = psGrid(by.css("table[class='n-grid']"));
numberField = grid.getQuickFilter(1);
numberField.click().sendKeys("Hello!");
*******************************************************************
it('test1', function () {
since('fail1').expect(numberField.getInputText()).toEqual("");
});
it('test2', function () {
since('fail2').expect(numberField.getInputText()).toEqual("Hello!");
});
});
Maybe somebody have an idea how I can do this?
To answer your question, if you want to run your code once before all tests then use beforeAll() function available in Jasmine 2. Here's a sample -
beforeAll(function(){
//Write your code here that you need to run once before all specs
});
You can use beforeEach() function available in Jasmine to run it each time before a test spec. Here's a sample -
beforeEach(function(){
//Write your code here that you need to run everytime before each spec
});
If you are facing issue in getting these functions to work, then update your plugins to latest version and then try running it. Also use the framework: 'jasmine2' in your conf.js file
Hope this helps.
I'm new to unit testing, so please forrgive me if my question could be silly. I wrote an unit test using Mocha with PhantomJS and Chai as assertion library. The code that I want to test is the following function:
function speakingNotification(audioStream){
var options = {};
var speechEvents = hark(audioStream, options);
speechEvents.on('speaking', function() {
return 'speaking';
});
speechEvents.on('stopped_speaking', function() {
return 'stopped_speaking';
});
}
As you can see it takes an audioStream parameter as input and then use a librabry called hark.js https://github.com/otalk/hark for detecting speaking events. The function should return if the user is speaking or not.
So I wrote the following unit test:
describe('Testing speaking notification', function () {
describe('Sender', function(){
var audio = document.createElement('audio');
audio.src = 'data:audio/mp3;base64,//OkVA...'; //audio file with sound
var noAudio = document.createElement('audio');
noAudio.src = 'data:audio/mp3;base64,...'; //audio file with no sound
it('should have a function named "speakingNotification"', function() {
expect(speakingNotification).to.be.a('function');
});
it('speaking event', function () {
var a = speakingNotification(audio);
this.timeout( 10000 );
expect(a).to.equal('speaking');
});
it('stoppedSpeaking event', function () {
var a = speakingNotification(noAudio);
this.timeout( 10000 );
expect(a).to.equal('stopped_speaking');
});
});
});
The test fails and shows:
AssertionError: expected undefined to equal 'speaking'
AssertionError: expected undefined to equal 'stopped_speaking'
I also tried to use done() insted of the timeout, however the test fails and shows:
ReferenceError: Can't find variable: done
I searched for tutorials, however I can only find simple examples that don't help.
How can I write a correct test?