Pass data from one it block to another - javascript

As you can see in the above image, controller value assigned in first it block is not the same anymore in second it block. Is this the default behavior? Can we do some changes in karma config file to get rid of this issue?
This project is based on angularjs 1.7.9 which is using karma and jasmine.

I think you can take advantage of beforeEach.
describe('Login', function () {
beforeEach(() => {
$controller.name = 'Pankaj';
});
it('test one', function () {
console.log($controller.name);
});
it('test two', function () {
console.log($controller.name);
});
});
beforeEach runs before every it block.
Edit
You have to use describe, beforeEach, beforeAll, afterEach, and afterAll appropriately. describe you can split by features/situations, beforeEach runs before every it, beforeAll runs once in a describe block, afterEach runs after each it in a describe block, and afterAll runs once after all the it blocks completed in a desribe block.
describe('Login', function () {
beforeEach(() => {
$controller.name = 'Pankaj';
});
it('test one', function () {
console.log($controller.name);
});
it('test two', function () {
console.log($controller.name);
});
describe('dynamic part', function () {
beforeEach(() => {
// call the function that will change the variable
});
it('test 1', function () {
// your assertions
});
it('test 2', function () {
// your assertions
});
});
});

Related

Mocha Does Not Run Second describe()

I am writing a test coverage for my code base and just started using Mocha/Chai for my backend. For whatever reason I can't get my second describe() to run in this function. I don't receive any error, it just exits after running the first describe() suite.
export async function testCreateUnknownCustomer(billCodeTest) {
let unknownRecordTest;
describe("Create A Unknown Customer Record", function () {
it("Creates a new unknown customer", async function () {
unknownRecordTest = await CustomersController.createUnknownCustomer(
'+15555551111',
billCodeTest
)
})
it('Should Be a Instance of a Sequelize Model', function () {
expect(unknownRecordTest instanceof Model).equals(true);
})
});
describe("Hard Delete unknown customer record", function () {
const unknownID = unknownRecordTest.customer_id;
it("Deletes a customer record", async function () {
console.log(await unknownRecordTest.destroy());
console.log(unknownRecordTest);
})
});
}
Leaving the describes raw in the file seems to have fixed everything. As opposed to wrapping it in a function that is exported and run in a main.test.js execution file. I don't really have a technical explanation why secondary describes wouldn't have executed regardless.

Jasmine - how to test functions that should only be called once (executed via a closure variable)

I'm using a closure to ensure that something is only called once:
var pageDOM = (function() {
var mounted = false
return {
initializePage: function() {
if (mounted == false) {
pageDOM.addBoxes();
mount = true
}
pageDOM.otherInitProcedures();
},
otherFunction: function() {
}
}
})();
I'm not sure what's the right way of thinking about unit testing pageDOM.initializePage. Jasmine specs are run in random order, and I think it's important to keep this for testing integrity (i.e., I would NOT want to impose order). This is my spec code:
describe("pageDOM", function() {
describe("initializePage", function() {
beforeEach(function() {
spyOn(pageDOM, "addBoxes")
spyOn(pageDOM, "otherInitProcedures")
})
describe("calling initializePage first time", function() {
beforeEach(function() {
pageDOM.initializePage();
})
it("should call both functions", function() {
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).toHaveBeenCalled()
})
describe("calling initializePage again", function() {
beforeEach(function() {
pageDOM.initializePage();
})
it("should only call otherInitProcedures", function() {
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).not.toHaveBeenCalled()
})
})
})
})
})
The problem is that if the specs don't run in order, then both will fail. What's a way to test this, or should I even try to test this?
I would assign the spies to variables and reset the spies in an afterEach hook.
Something like this (follow the !! in the comments):
describe("pageDOM", function() {
describe("initializePage", function() {
// !! initialize these variables
let addBoxesSpy;
let otherInitProceduresSpy;
beforeEach(function() {
// !! assign the variables
addBoxesSpy = spyOn(pageDOM, "addBoxes")
otherInitProceduresSpy = spyOn(pageDOM, "otherInitProcedures")
})
describe("calling initializePage first time", function() {
beforeEach(function() {
pageDOM.initializePage();
})
it("should call both functions", function() {
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).toHaveBeenCalled()
})
describe("calling initializePage again", function() {
beforeEach(function() {
pageDOM.initializePage();
})
it("should only call otherInitProcedures", function() {
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).not.toHaveBeenCalled()
})
})
})
// !! Reset the spies in an afterEach
afterEach(() => {
addBoxesSpy.calls.reset();
otherInitProceduresSpy.calls.reset();
});
})
})
After resetting the calls to what you're spying on, order should not matter anymore.
So your "pageDOM" method is state full, so why use 2 times describe and set the call to "initializePage" method every time by hooking it in beforeEach, doesn't make sense. Instead you can do like this -
describe("pageDOM:initializePage", function() {
describe("calling initializePage first time", function() {
beforeEach(function() {
spyOn(pageDOM, "addBoxes");
spyOn(pageDOM, "otherInitProcedures");
})
it("should call both functions", function() {
pageDOM.initializePage();
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).toHaveBeenCalled()
})
it("should only call otherInitProcedures", function() {
pageDOM.initializePage();
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).not.toHaveBeenCalled()
})
})
})
Jasmine executes it blocks within a describe sequentially and you can get the desired checks as well. Working stackblitz link for you(ignore other testcases)
To make the code more testable, I would not initiate the function immediately.
var pageDOMConstructor = vfunction() {
var mounted = false
return {
initializePage: function() {
if (mounted == false) {
pageDOM.addBoxes();
mount = true
}
pageDOM.otherInitProcedures();
},
otherFunction: function() {
}
}
};
var pageDOM = pageDOMConstructor();
then you can test pageDOMConstructor easily.
To check how often something has been called you can use toHaveBeenCalledTimes
this is not quite complete and might need some small changes, its just to give you an idea of how to solve this:
describe("pageDOMConstructor", function () {
describe("initializePage", function () {
// setup variable, its a let because it will be reset before every test
let pageDom;
beforeEach(function () {
pageDom = pageDOMConstructor();
spyOn(pageDOM, "addBoxes");
spyOn(pageDOM, "otherInitProcedures");
});
it("should call both functions when calling initializePage first time", function () {
pageDOM.initializePage();
expect(pageDOM.otherInitProcedures).toHaveBeenCalledTimes(1);
expect(pageDOM.addBoxes).toHaveBeenCalledTimes(1);
});
it("should only call otherInitProcedures when calling initializePage again", function () {
pageDOM.initializePage();
// you could remove these two lines because they are in the other test
expect(pageDOM.otherInitProcedures).toHaveBeenCalledTimes(1);
expect(pageDOM.addBoxes).toHaveBeenCalledTimes(1);
pageDOM.initializePage();
expect(pageDOM.otherInitProcedures).toHaveBeenCalledTimes(1);
expect(pageDOM.addBoxes).not.toHaveBeenCalledTimes(1);
});
});
});
Each it should be treated like a separate test, and they should be completely self reliant - with some exceptions like beforeEach
When code is hard to test, it's often a sign that you could benefit from refactoring it a bit. I have found that testable code equals usable and flexible code in production.

Pass a Jasmine spec on async timeout

Running Jasmine 2.8.
I've got a test case in which the failure case is an event handler being triggered under a condition when it should not be. Note that the codebase that provides events here is part of a proprietary, developed-in-house system and as such these are not DOM events and I am not leveraging any popular JS frameworks:
it('should trigger the event handler in state 0', function (done) {
function specCb(ev) {
expect(ev).toBeDefined();
expect(ev.data).toBe('some value');
done();
}
thing.state = 0;
simulateEventTrigger(thing, specCb);
});
it('should not trigger the event handler in state 1', function (done) {
function specCb(ev) {
done.fail('this should not be called in state 1');
}
thing.state = 1;
simulateEventTrigger(thing, specCb);
});
The second spec will always fail because either the callback is called, which explicitly fails the spec, or the spec times out waiting for done() to be called, thus failing it. How do I get Jasmine to pass a spec if it times out?
Check out spies in Jasmine. Spies allow you to "spy on" a function and assert whether it has been called and with which arguments. Or, in your case, whether it has not been called. An example might look like this...
describe("A spy", function() {
var evHandlers;
beforeEach(function() {
evHandlers = {
callback: function (e) {}
}
spyOn(evHandlers, 'callback');
});
it('should not trigger the event handler in state 1', function (done) {
thing.state = 1;
simulateEventTrigger(thing, evHandlers.callback);
expect(evHandlers.callback).not.toHaveBeenCalled();
});
});
Actually it function accepts the callback so you could do something like this:
const TEST_TIMEOUT = 1000;
const CONSIDER_PASSED_AFTER = 500;
describe('a test that is being considered passing in case of a timeout', () => {
it('should succeed in after a specified time interval', done => {
setTimeout(() => {
done();
}, CONSIDER_PASSED_AFTER);
}, TEST_TIMEOUT);
});

How to run Mocha tests in a defined order

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.

load data from module before test executes

(I asked this question recently and accepted an answer but it's still not what I need.) I really need to create dynamic tests from data loaded from a module. Each item from the array will have it's own describe statement with certain protractor actions. My previous post has an answer that says to use an it statement, but I can't do that because there's too much going on.
My main problem is that the data doesn't get loaded in time for the describe. I had another suggestion to use VCR.js or something similar but I don't think those will work because I'm using a module. Is there a way I can save the data to a separate file and load it in? Would that be a good way to go?
var data = require('get-data'); //custom module here
describe('Test', function() {
var itemsArr;
beforeAll(function(done) {
data.get(function(err, result) {
itemsArr = result; //load data from module
done();
});
})
//error: Cannot read property 'forEach' of undefined
describe('check each item', function() {
itemsArr.forEach(function(item) {
checkItem(item);
});
});
function checkItem (item) {
var itemName = item.name;
describe(itemName, function() {
console.log('describe');
it('should work', function() {
console.log('it');
expect(true).toBeTruthy();
});
});
}
});
UPDATE:
I used Eugene's answer and came up with this. I can't test each individual study how I want because the it statement doesn't fire. Is this problem even solvable??
describe('check each item', function () {
it('should load data', function (done) {
browser.wait(itemsPromise, 5000);
itemsPromise.then(function(itemsArr) {
expect(itemsArr).toBeTruthy();
studyArr = itemsArr.filter(function (item) {
return item.enabled && _.contains(item.tags, 'study');
});
studyCount = studyArr.length;
expect(studies.count()).toEqual(studyCount);
checkItems(studyArr);
done();
});
});
function checkItems (itemsArr) {
itemsArr.forEach(function (item) {
describe(item.id, function () {
console.log('checkItems', item.id);
// doesn't work
it('should work', function (done) {
expect(false).toBeTruthy();
done();
});
});
});
}
});
You're trying to do something that Jasmine does not allow: generating tests after the test suite has started. See this comment on an issue of Jasmine:
Jasmine doesn't support adding specs once the suite has started running. Usually, when I've needed to do this, I've been able to know the list of options ahead of time and just loop through them to make the it calls. [...]
("adding specs" === "adding tests")
The point is that you can generate tests dynamically but only before the test suite has started executing tests. One corollary of this is that the test generation cannot be asynchronous.
Your second attempt does not work because it is trying to add tests to a suite that is already running.
Your first attempt is closer to what you need but it does not work either because describe calls its callback immediately, so beforeAll has not run by the time your describe tries to generate the tests.
Solutions
It all boils down to computing the value of itemsArr before the test suite start executing tests.
You could create a .getSync method that would return results synchronously. Your code would then be something like:
var data = require('get-data'); //custom module here
var itemsArr = data.getSync();
describe('Test', function() {
describe('check each item', function() {
itemsArr.forEach(function(item) {
checkItem(item);
});
});
[...]
If writing .getSync function is not possible, you could have an external process be responsible for producing a JSON output that you could then deserialize into itemsArr. You'd execute this external process with one of the ...Sync functions of child_process.
Here's an example of how the 2nd option could work. I've created a get-data.js file with the following code which uses setTimeout to simulate an asynchronous operation:
var Promise = require("bluebird"); // Bluebird is a promise library.
var get = exports.get = function () {
return new Promise(function (resolve, reject) {
var itemsArr = [
{
name: "one",
param: "2"
},
{
name: "two",
param: "2"
}
];
setTimeout(function () {
resolve(itemsArr);
}, 1000);
});
};
// This is what we run when were are running this module as a "script" instead
// of a "module".
function main() {
get().then(function (itemsArr) {
console.log(JSON.stringify(itemsArr));
});
};
// Check whether we are a script or a module...
if (require.main === module) {
main();
}
Then, inside the spec file:
var child_process = require('child_process');
var itemsArr = JSON.parse(child_process.execFileSync(
"/usr/bin/node", ["get-data.js"]));
describe('Test', function() {
itemsArr.forEach(function(item) {
checkItem(item);
});
function checkItem (item) {
var itemName = item.name;
describe(itemName, function() {
console.log('describe');
it('should work', function() {
console.log('it');
expect(true).toBeTruthy();
});
});
}
});
I've tested the code above using jasmine-node. And the following file structure:
.
├── data.js
├── get-data.js
└── test
└── foo.spec.js
./node_modules has bluebird and jasmine-node in it. This is what I get:
$ ./node_modules/.bin/jasmine-node --verbose test
describe
describe
it
it
Test - 5 ms
one - 4 ms
should work - 4 ms
two - 1 ms
should work - 1 ms
Finished in 0.007 seconds
2 tests, 2 assertions, 0 failures, 0 skipped
Try to use a promise, something like:
var deferred = protractor.promise.defer();
var itemsPromise = deferred.promise;
beforeAll(function() {
data.get(function(err, result) {
deferred.fulfill(result);
});
})
And then:
describe('check each item', function() {
itemsPromise.then(function(itemsArr) {
itemsArr.forEach(function(item) {
checkItem(item);
});
});
});
Another solution I can think of is to use browser.wait to wait until itemsArr becomes not empty.
Is your get-data module doing some browser things with protractor? If so, you will need to set/get itemsArr within the context of the controlFlow. Otherwise it will read all the code in the get-data module, but defer its execution and not wait for it to finish before moving right along to those expect statements.
var data = require('get-data'); //custom module here
var itemsArr;
describe('Test', function() {
beforeAll(function() {
// hook into the controlFlow and set the value of the variable
browser.controlFlow().execute(function() {
data.get(function(err, result) {
itemsArr = result; //load data from module
});
});
});
//error: Cannot read property 'forEach' of undefined
describe('check each item', function() {
// hook into the controlFlow and get the value of the variable (at that point in time)
browser.controlFlow().execute(function() {
itemsArr.forEach(function(item) {
checkItem(item);
});
});
});
function checkItem (item) {
var itemName = item.name;
describe(itemName, function() {
console.log('describe');
it('should work', function() {
console.log('it');
expect(true).toBeTruthy();
});
});
}
});

Categories

Resources