Isolating js files that are needed for one test suite - javascript

I am using karma/mocha/chrome headless for my test stack.
--js 1
document.addEventListener('test', ...);
do sth more.
--js 2
document.addEventListener('test', ...);
do sth more differently.
-- test suite 1
require(test1)
i am dispatching the test event here to test js 1
-- test suite 2
require(test2)
I am dispatchinh the test event here to test js 2
The problem is that both of these files will be now available globally. When I am running the test suite 2, both js1 and js2 events are going to listen my dispatch because js1 will be still globally available.
My perfect scenario would be. Load js1 to test suite 1 and encapsulate it there. Hacky way would be remove it after the tests are ran.
My question is, how to make sure that needed js files are loaded locally in the scope of one test suite? Basically load only nesseccary files for the specific test suite and keep them isolated from rest of test suites.
Thanks.

If I understand the question correctly, you want to scope your modules to the relevant test file?
If that's the case then you can load your modules like so:
const sharedModule = require('some/shared/module');
describe('some test', function() {
const scopedModule = require('some/path/to/module');
it('should...', function() {
// Test code...
})
)
This would result in your module being loaded only when the test begins to execute, being scoped inside that block.

Related

Karma detects changes but does not run new tests

I am using babel and ng-html2js to build and test an angular app with Karma as the test runner. For the most part, things are working. If I make changes to the files, Karma will rerun the tests and pass/fail the tests as appropriately. Karma also determines when changes have been made to the test files themselves ... however, it will run the old tests rather than the new tests.
As a simple example:
/* constant.js */
angular.module("foo").constant("constant", 1);
/* constant.test.js */
let constant;
beforeEach(inject(_constant_ => constant = _constant));
describe("constant", () => {
it("is 1", () => {
expect(constant).to.equal(2);
});
});
In the above test, I am checking constant against 2, but in my original code, constant is 1.
If I change constant to 2 in constant.js, the test will rerun and pass.
If I change .to.equal(2) to .to.equal(1) and rerun the tests from scratch, they will pass.
If I change .to.equal(2) to .to.equal(1) while the tests are already running, the tests will rerun automatically but they will fail because it is still checking 1 to equal 2.
How can I get Karma to rerun the new tests when I make changes to the test files?

Webdriver.io crashes with NoSessionIdError

I'm trying to get webdriver.io and Jasmine working.
Following their example, my script is at test/specs/first/test2.js (in accordance with the configuration) and contains:
var webdriverio = require('webdriverio');
describe('my webdriverio tests', function() {
var client = {};
jasmine.DEFAULT_TIMEOUT_INTERVAL = 9999999;
beforeEach(function() {
client = webdriverio.remote({ desiredCapabilities: {browserName: 'firefox'} });
client.init();
});
it('test it', function(done) {
client
.url("http://localhost:3000/")
.waitForVisible("h2.btn.btn-primary")
.click("h2.btn.btn-primary")
.waitForVisible("h2.btn.btn-primary")
.call(done);
});
afterEach(function(done) {
client.end(done);
});
});
I'm using wdio as the test runner, and set it up using the interactive setup. That config is automatically-generated and all pretty straightforward, so I don't see a need to post it.
In another terminal window, I am running selenium-server-andalone-2.47.1.jar with Java 7. I do have Firefox installed on my computer (it blankly starts when the test is run), and my computer is running OS 10.10.5.
This is what happens when I start the test runner:
$ wdio wdio.conf.js
=======================================================================================
Selenium 2.0/webdriver protocol bindings implementation with helper commands in nodejs.
For a complete list of commands, visit http://webdriver.io/docs.html.
=======================================================================================
[18:17:22]: SET SESSION ID 46731149-79aa-412e-b9b5-3d32e75dbc8d
[18:17:22]: RESULT {"platform":"MAC","javascriptEnabled":true,"acceptSslCerts":true,"browserName":"firefox","rotatable":false,"locationContextEnabled":true,"webdriver.remote.sessionid":"46731149-79aa-412e-b9b5-3d32e75dbc8d","version":"40.0.3","databaseEnabled":true,"cssSelectorsEnabled":true,"handlesAlerts":true,"webStorageEnabled":true,"nativeEvents":false,"applicationCacheEnabled":true,"takesScreenshot":true}
NoSessionIdError: A session id is required for this command but wasn't found in the response payload
at waitForVisible("h2.btn.btn-primary") - test2.js:21:14
/usr/local/lib/node_modules/webdriverio/node_modules/q/q.js:141
throw e;
^
NoSessionIdError: A session id is required for this command but wasn't found in the response payload
0 passing (3.90s)
$
I find this very strange and inexplicable, especially considering that it even prints the session ID.
Any ideas?
Please check out the docs on the wdio test runner. You don't need to create an instance using init on your own. The wdio test runner takes care on creating and ending the session for you.
Your example covers the standalone WebdriverIO usage (without testrunner). You can find examples which use wdio here.
To clarify that: there are two ways of using WebdriverIO. You can embed it in your test system by yourself (using it as standalone / or as a scraper ). Then you need to take care of things like create and end an instance or run those in parallel. The other way to use WebdriverIO is using its test runner called wdio. The testrunner takes a config file with a bunch of information on your test setup and spawns instances updates job information on Sauce Labs and so on.
Every Webdriver command gets executed asynchronously.
You properly called the done callback in afterEach and in your test it test, but forgot to do it in beforeEach:
beforeEach(function(done) {
client = webdriverio.remote({ desiredCapabilities: {browserName: 'firefox'} });
client.init(done);
});

Loading templates

Good day,
I've tried using both ng-html2js and grunt-html2js to try to load my directive templates as modules when testing but have hit a-bit of a road block on both cases. My questions will revolve around the former as this is what I've attempting to use most.
Correct me if I'm wrong; In order to use ng-html2js you must first register the module from node like so:
npm install karma-ng-html2js-preprocessor -g
This installs it globally which is what I want as I run my karma
tests using the following command which hands over to the global instance of karma installed.:
karma start
Next in the karma.conf.js file you must then specify which templates you
want to use and what dependency it is to be used with:
preprocessors: {
'webapp/scripts/*.js': 'coverage',
'webapp/modules/**/*.js': 'coverage',
'webapp/modules/groupbydrug.html': 'ng-html2js'
},
When it comes to the test, we can load the module doing this:
beforeEach(module('webapp/modules/groupbydrug.html'));
Finally we compile as usual, add the directive to a piece of html and test:
beforeEach(function () {
ele = angular.element('<div group-by-drug=""></div>');
compile(ele)(scope);
scope.$digest();
});
it('should have a table with a class of "drugs-by-mail"', function () {
var div = ele.find('div.outrepeat');
console.log(div);
});
BTW - The template looks like this:
<div ng-repeat="(orderNumber,orderData) in orders" class="drugs-by-mail">
<table class="recent-order-mail">
...
</table>
</div>
Based on what I've read I should beable to retrieve the div from the template but when I try and print it to the console I get an empty object (Object{}) when I'd actually expect to see all the content within that div - is that not correct?
So where along here am I going wrong? Or is this an issue with installation (global karma vs local)
Thanks

How to set execution order of mocha test cases in multiple files

I have two javascript files which contain mocha test cases.
//----------abc.js -------------
describe("abc file", function(){
it("test 1" , function(){
assert.equal(20 , 20);
});
});
//---------xyz.js--------------
describe("xyz file", function(){
it("test 1" , function(){
assert.equal(10 , 10);
});
});
I have put them in a folder called test and when I execute the mocha command the first file(abc.js) is always executed before xyz.js.
I thought this might be due to alphabetical ordering and renamed the files as
abc.js => xyz.js
xyz.js => abc.js
but still, the content of the xyz.js (previously abc.js) is executed first. How can I change the execution order of these test files?
In the second file, require the first one:
--- two.js ---
require("./one")
or if you are using ES modules:
--- two.js ---
import "./one"
Mocha will run the tests in the order the describe calls execute.
I follow a totally seperate solution for this.
Put all your tests in a folder named test/ and
Create a file tests.js in the root directory in the order of execution
--- tests.js ---
require('./test/one.js')
require('./test/two.js')
require('./test/three.js')
And in the tests files one.js, two.js and so on write your simple mocha tests
this way if you want to run them in the order you have defined then just run mocha tests.js
Mocha has a --sort (short -S) option that sorts test files:
$ mocha --help
[...]
-S, --sort sort test files
[...]
Since mocha sorts files in alphabetical order, I usually prefix my test files names with numbers, like:
0 - util.js
1 - something low level.js
2 - something more interesting.js
etc.
In addition to being really easy to maintain (no gulp grunt or any of that nonsense, no editing your package.json...), it provides the benefit that:
people reading your source code get an idea of the structure of your program, starting from the less interesting parts and moving up to the business layer
when a test fails, you have some indication of causality (if something failed in 1 - something.js but there are no failures in 0 - base.js then it's probably the fault of the layer covered by 1 - something.js
If you're doing real unit tests of course order should not matter, but I'm rarely able to go with unit tests all the way.
If you prefer a particular order, you can list the files (in order) as command-line arguments to mocha, e.g.:
$ mocha test/test-file-1.js test/test-file-2.js
To avoid a lot of typing every time you want to run it, you could turn this into an npm script in your package.json:
{
// ...
"scripts": {
"test": "mocha test/test-file-1.js test/test-file-2.js"
}
// ...
}
Then run your suite from the command line:
$ npm test
Or if you're using Gulp, you could create a task in your gulpfile.js:
var gulp = require('gulp');
var mocha = require("gulp-mocha");
gulp.task("test", function() {
return gulp.src([
"./test/test-file-1.js",
"./test/test-file-2.js"
])
.pipe(mocha());
});
Then run $ gulp test.
The way it worked for my tests to be executed in a specific order was to create a separate test.js file and then added a describe for each mocha test file I'd wanted to execute.
test.js:
describe('test file 1', function() {
require('./test1.js')
})
describe('test file 2', function() {
require('./test2.js')
})
Then simply run mocha test.js
I am exporting an array with all required files and that is the way I tell mocha the order of execution through index.js file in the folder with all my test files:
const Login = require('../login');
const ChangeBudgetUnit = require('./changeBudgetUnit');
const AddItemsInCart = require('./addItemsInCart');
// if the order matters should export array, not object
module.exports = [
Login,
ChangeBudgetUnit,
AddItemsInCart
];
mocha-steps allows you to write tests that run in a specific sequence, aborting the run at the first failure. It provides a drop-in replacement for it, called steps.
Example usage:
describe('my smoke test', async () => {
step('login', async () => {})
step('buy an item', async () => throw new Error('failed'))
step('check my balance', async () => {})
xstep('temporarily ignored', async () => {})
})
The repo hasn't seen much activity in three years, but it works fine with Mocha 9.

how to reset a requirejs module between unit tests

I have a JavaScript project which I want to observe the TDD methodology. I chose the karma framework and requirejs library for this and followed an example demonstrated in the karma docs here.
There is an example of one unit-test file, which is:
define(['app', 'jquery', 'underscore'], function(App, $, _) {
describe('just checking', function() {
it('works for app', function() {
var el = $('<div></div>');
var app = new App(el);
app.render();
expect(el.text()).toEqual('require.js up and running');
});
it('works for underscore', function() {
// just checking that _ works
expect(_.size([1,2,3])).toEqual(3);
});
});
});
The main problem with this approach is that there is no way to clear the App module for each test. So if there are some changes in the App (like closure variables changes etc.) in one it call, then they can affect other it call.
Could anyone suggest something on this? These are such popular tools, I can't believe no one ever ran into such a situation.
So, to recapitulate the question, I would like to ask for an answer for any of these more specific ones:
is there any way to "reset" (clear) module in requirejs? (actually, I suppose that there is no such a way, except reload all modules once again)
is there any better approach to run karma and requirejs, so that modules do not have any remains (side effects) of other tests (it function calls) on them ?
With some help from Google I've found a solution to this. I cannot say that it is ideal, but it works at least and in each test a required module is in it's pristine state.
First, one need to have his main.js test file like this, it is almost the same as in the docs, except tests are not defined as modules and are not included as dependencies:
require.config({file
baseUrl: '/base',
paths: {},
shim: {},
deps: [],
callback: window.__karma__.start
});
In the main karma config must be present this (one need to adapt his own paths)
files: [
{pattern: 'src/**/*.js', included: false},
'tests/unit/**/*.js'
],
And in the tests themselves we leverage jasmine async tests capabilities and requirejs.undef method for "clearing" a module (which will reset the loader's internal state to forget about the previous definition of the module, as it is stated in the docs).
This method has one implication, that it will not reset other modules, which are already loaded and depend on the reset module, but this should not be a problem, if one writes his tests in a right way, that is he tests only one module (and may be some parent ones, from which the child inherits some behavior), so that it should not be hard to undef several modules at ones.
describe('Some/Module tests', function() {
'use strict'
var M
beforeEach(function(done) {
requirejs.undef('src/Some/GParentModule')
requirejs.undef('src/Some/ParentModule')
requirejs.undef('src/Some/Module')
require(['src/Some/Module'], function(m) {
M = m
done()
})
})
it('some description', function() {
var i = 0
for (var prop in M) {
i++
}
expect(i).toEqual(1)
expect(prop).toEqual('init')
expect(M.init).toEqual(jasmine.any(Function))
})
})
I've checked this, between it calls changes to the module do not persist, so I suppose, it's safe to use this approach.
Thanks everyone for help, if someone has a better way, please answer here.
is there any way to "reset" (clear) module in requirejs?
You can use requirejs.undef(), but the documentation mention some gotchas. And to my knowledge there is no call you can use to say "unload this module and everything it depends on", which in a complex application is probably what is needed.
is there any better approach to run karma and requirejs, so that modules do not have any remains (side effects) of other tests (it function calls) on them ?
The better approach is to design your application so that state is tied to an object you instantiate rather than to the module itself. This is what I do in the test suite for a large application of mine. The beforeEach hook resets what requires resetting and instantiates the application anew for each test.

Categories