I have of couple JS modules, let's say A and B. In module A, I have a method which depends on another method imported from module B. Now how do I test with sinon.spy, whether the method from A triggers the method from B?
//ModuleA.js
import{ methodFromB } from "ModuleB.js";
function methodFromA (){
methodFromB();
}
export{
methodFromA
}
//ModuleB.js
function methodFromB (){
//doSomething
}
ModuleA.Spec.js
import sinon from 'sinon';
import { assert,expect } from "chai";
import * as modB from "ModuleB.js";
import { methodA } from '../js/ModuleA.js';
describe("ModuleA.js", function() {
beforeEach(function() {
stubmethod = sinon.stub(modB, "methodB").returns("success");
});
after(function() {
});
describe("#methodA", function() {
it("Should call the method methodB", function() {
expect(methodA()).to.deep.equal('success');
expect(stubmethod.calledOnce).to.equal(true);
});
});
});
After trying to stub methodB, i get the error "expected undefined to deeply equal 'success'".
Thanks in advance.
you should mock module B, and expect it to.have.been.called instead of spy from methodFromA
You stub the wrong function from module B. It is supposed to be methodFromB not methodB based on your source file.
describe("ModuleA.js", function () {
beforeEach(function () {
stubmethod = sinon.stub(modB, "methodFromB").returns("success"); // change to methodFromB
});
after(function () {
stubmethod.restore(); // don't forget to restore
});
describe("#methodA", function () {
it("Should call the method methodB", function () {
expect(methodA()).to.deep.equal('success');
expect(stubmethod.calledOnce).to.equal(true);
});
});
});
Related
I have a VueJS component text-editor that I call as such in my tests:
import Vue from 'vue';
import TextEditor from '../src/TextEditor.vue';
import expect, {spyOn} from 'expect';
describe("<text-editor>", (done) => {
beforeEach(() => {
this.vm = new Vue({
data: { hi: { text: "hi" } },
template: '<div><text-editor ref="texteditor" v-model="hi" #hide="hidecb"></text-editor></div>',
components: { 'text-editor': TextEditor },
methods: {
hidecb: function() {
console.log('hide');
}
}
}).$mount();
});
it("should call the #hide callback", (done) => {
let spy = spyOn(this.vm, 'hidecb');
// This call will trigger an this.$emit('hide') in the component
this.vm.$refs.texteditor.save();
Vue.nextTick(() => {
expect(spy).toHaveBeenCalled();
done();
});
});
});
Weird thing is, method hidecb does get called and I see "hide" on the console. But the spy will fail with the following error:
Error: spy was not called (undefined:29)
I checked and the callback is indeed called before nextTick. I should also point out that I am using Webpack, expectjs and karma.
Would someone be able to point out why the spy doesn't work?
I have a function that makes an AJAX call to a service. I'm attempting to expect that the displayError function is called on a failure.
I have my function ajaxCall that accepts a url. Upon success I pass the result to displaySuccess and when there's an error I pass the details to displayError.
function ajaxCall(url) {
$.ajax({
method: "GET",
url: url,
data: "json",
beforeSend: function (xhr) {
//Do Stuff
},
error: function(xhr, textStatus, errorThrown) { displayError(xhr, textStatus, errorThrow, url)},
success: function (results) { displaySuccess(result) }
});
}
function displayError(xhr, textStatus, errorThrow, url) {
//Do Stuff//
}
function displaySuccess(results) {
//Do Stuff//
}
In Jasmine I have it successfully verifying the URL. My problem is in testing to insure that the displayError and displaySuccess functions are called.
I have the following for this specific issue so far.
describe('The ajaxCall component', function() {
it('should call the error function when the ajax call fails', function () {
var obj = {};
spyOn(obj, 'displayError');
spyOn($, "ajax").and.callFake(function (options) {
options.error();
});
ajaxCall('/myResource/get');
expect(obj.method).toHaveBeenCalled();
});
}
I'm a little new to unit testing and I've searched trying to find suggestions that would help but they make the unit test fail. Where am I going wrong with this?
This all boils down to how you spy on your objects and writing code that is more testable. Let's work through a few strategies.
Strategy 1
Given your current code is not within an object, you could test that these functions are called by simply testing their implementation directly.
Instead of testing that the functions were called, you would test their implementation directly.
Example
describe("strategy 1", function () {
var ajaxSpy;
beforeEach(function () {
ajaxSpy = spyOn($, 'ajax');
ajaxCall();
});
describe("error callback", function () {
beforeEach(function() {
spyOn(window, 'alert');
var settings = ajaxSpy.calls.mostRecent().args[0];
settings.error();
});
describe("when there is an error", function() {
it("should alert an error message", function() {
expect(window.alert).toHaveBeenCalledWith('Error');
});
});
});
});
Strategy 2
While the above works, it can be cumbersome to write tests. Ideally, you want to test the invocation and implementation separately.
To do so, we can spy on these functions. Since these are in the global namespace, you can spy on them through the window object.
Example
describe("strategy 2", function () {
var ajaxSpy;
beforeEach(function () {
ajaxSpy = spyOn($, 'ajax');
ajaxCall();
});
describe("error callback", function () {
beforeEach(function() {
spyOn(window, 'displayError');
var settings = ajaxSpy.calls.mostRecent().args[0];
settings.error();
});
describe("when there is an error", function() {
it("should alert an error message", function() {
expect(window.displayError).toHaveBeenCalled();
});
});
});
});
Strategy 3 (Recommended)
The final strategy, and what I recommend, has a similar setup to the second strategy, except we encapsulate our implementation into a custom object.
Doing so makes the code more testable by wrapping functionality in objects and avoids the global namespace (i.e. window).
Example
describe("solution 3", function() {
var ajaxSpy;
beforeEach(function() {
ajaxSpy = spyOn($, 'ajax');
ajaxService.ajaxCall();
});
describe("error callback", function() {
beforeEach(function() {
spyOn(ajaxService, 'displayError');
var settings = ajaxSpy.calls.mostRecent().args[0];
settings.error();
});
it("should alert an error message", function() {
expect(ajaxService.displayError).toHaveBeenCalled();
});
});
});
Background
I am working on a program in Node.js and writing my test suites in Mocha with Chai and SinonJS. I have core graphics module which controls access to a node-webgl context.
Due to how node-webgl works, I only wish to initialize a context once for the entire test run. I have some tests I wish to run prior to the initialization of the core module, like so:
describe('module:core', function () {
describe('pre-init', function () {
describe('.isInitialized', function () {
it('should return false if the module is not initialized', function () {
expect(core.isInitialized()).to.be.false;
});
});
describe('.getContext', function () {
it('should error if no context is available', function () {
expect(function () {
core.getContext();
}).to.throw(/no context/i);
});
});
});
describe('.init', function () {
it('should error on an invalid canvas', function () {
expect(function () {
core.init(null);
}).to.throw(/undefined or not an object/i);
expect(function () {
core.init({});
}).to.throw(/missing getcontext/i);
});
it('should error if the native context could not be created', function () {
var stub = sinon.stub(global._canvas, 'getContext').returns(null);
expect(function () {
core.init(global._canvas);
}).to.throw(/returned null/i);
stub.restore();
});
it('should initialize the core module', function () {
expect(function () {
core.init(global._canvas);
}).not.to.throw();
});
});
describe('post-init', function () {
describe('.isInitialized', function () {
it('should return true if the module is initialized', function () {
expect(core.isInitialized()).to.be.true;
});
});
describe('.getContext', function () {
it('should return the current WebGL context', function () {
var gl = null;
expect(function () {
gl = core.getContext();
}).not.to.throw();
// TODO Figure out if it's actually a WebGL context.
expect(gl).to.exist;
});
});
});
});
Then I can run the remaining tests.
Problem
When I run this through Mocha, everything is fine since the core test suite is the first thing to be run. My concern is that if any test suites get run before the core test suite, then those test suites will fail as the core is not initialized yet.
What is the best way to ensure the core test suite is always run before any other test suites?
In the end I refactored my code to permit the core module to be torn down without affecting node-webgl and using a before block to initialize it, like so:
// Run this before all tests to ensure node-webgl is initialized
before(function () {
if (!global._document) {
global._document = WebGL.document();
global._document.setTitle('Machination Graphics Test Suite');
}
if (!global._canvas) {
global._canvas = global._document.createElement('canvas', 640, 480);
}
});
describe('test suite goes here', function () {
// Ensure core is ready for us before testing (except when testing core)
before(function () {
if (!core.isInitialized()) {
core.init(global._canvas);
}
});
// Tear down core after all tests are run
after(function () {
core.deinit();
});
...
});
Use before() as described in their documentation.
describe('hooks', function() {
before(function() {
// runs before all tests in this block
});
......
});
the function in before will run first and everything else int he describe after it.
hope this helps.
I am trying to learn the Jasmine spyOn function. I get TypeError: Cannot read property 'fail' when using spyOn to test a function that calls jQuery.getJSON.
Here is the function I want to test:
getJsonServerNine: function () {
'use strict';
$.getJSON(queryServer.url, function(simpleJson) {
parseResponse(simpleJson);
}).fail(function(error) {
console.log(error.statusText);
});
}
Here is my test:
it("tests getJSON call", function() {
spyOn($, 'getJSON').and.callFake(function (url, success) {
success({
"nine": 9
});
});
queryServer.getJsonServerNine();
expect(queryServer.queryResult).toBe(9);
});
If I remove the fail callback from getJsonServerNine() then my code works. If I call jquery.ajax() instead of getJSON then I can find a way to get it to work. How can can I get it to work with a call to $.getJSON?
I have seen this answer to a similar question and it is very nice but does not closely match my case, does not work for me, and uses outdated Jasmine syntax.
The object returned by $.getJSON should have a fail() method. In your implementation it returns undefined.
The fake function should return an object like so:
// Source
var queryServer = {
getJsonServerNine: function () {
'use strict';
$.getJSON(queryServer.url, function (simpleJson) {
// parseResponse(simpleJson);
console.log(simpleJson)
queryServer.queryResult = simpleJson[Object.keys(simpleJson)[0]];
}).fail(function (error) {
console.log(error.statusText);
});
}
}
// Test
describe('foo', function () {
it("tests getJSON call", function () {
spyOn($, 'getJSON').and.callFake(function (url, success) {
success({
"nine": 9
});
return {
fail: function() {}
}
});
queryServer.getJsonServerNine();
expect(queryServer.queryResult).toBe(9);
});
});
See JS fiddle here - http://jsfiddle.net/eitanp461/32e17uje/
I am trying to test a function that requires a module using jasmine and requirejs.
Here is a dummy code:
define("testModule", function() {
return 123;
});
var test = function() {
require(['testModule'], function(testModule) {
return testModule + 1;
});
}
describe("Async requirejs test", function() {
it("should works", function() {
expect(test()).toBe(124);
});
});
It fails, because it is an async method. How can I perform a test with it?
Note: I dont want to change my code, just my tests describe function.
For testing of an asynchronous stuff check runs(), waits() and waitsFor():
https://github.com/pivotal/jasmine/wiki/Asynchronous-specs
Though this way looks a bit ugly as for me, therefore you could also consider following options.
1. I'd recommend you to try jasmine.async that allows you to write asynchronous test cases in this way:
// Run an async expectation
async.it("did stuff", function(done) {
// Simulate async code:
setTimeout(function() {
expect(1).toBe(1);
// All async stuff done, and spec asserted
done();
});
});
2. Also you can run your tests inside require's callback:
require([
'testModule',
'anotherTestModule'
], function(testModule, anotherTestModule) {
describe('My Great Modules', function() {
describe('testModule', function() {
it('should be defined', function() {
expect(testModule).toBeDefined();
});
});
describe('anotherTestModule', function() {
it('should be defined', function() {
expect(anoterTestModule).toBeDefined();
});
});
});
});
3. Another point is I guess that this code is not working as you're expecting:
var test = function() {
require(['testModule'], function(testModule) {
return testModule + 1;
});
};
Because if you call test(), it won't return you testModule + 1.