Monkey-patching jest object for all test files - javascript

How can I monkey-patch some methods in the global jest object for all test files at once? I don't want to add any extra code to my test files, it has to be done somewhere in setup and it can be an ugly hack.
I tried doing that from a custom environment, setupFiles and setupFilesAfterEnv, but it looks like they all get a different instance of jest object and my changes aren't visible in test files.
Disclaimer: I know that it's a bad thing to do but I need it for some one-time benchmarking only and it's the easiest solution that gets the job done.

I got this working! You're right, Jest does re-construct the global jest object for every test case, but you can override the function it uses to do that. In jest.config.js, set globalSetup to something like <rootDir>/jest-global-setup.js. Then, in jest-global-setup.js, add this:
const jestRuntime = require('jest-runtime');
const { _createJestObjectFor } = jestRuntime.prototype;
jestRuntime.prototype._createJestObjectFor = function(...args) {
// Call the original function to create a normal jest object.
const jestObject = _createJestObjectFor.apply(this, args);
// Apply your changes.
jestObject.isMonkeyPatched = true;
// Return the patched object.
return jestObject;
}
// Jest expects to find a function of some sort as well,
// but we don't need it for this example.
module.exports = function() { /* do nothing */ }

Related

Is it possible to have these functions exposed for testing?

Given that I'm extending an existing module and it uses the module.exports in the way shown below, can I even call the start methods from (mocha) tests?
I suspect that there's no decent way to tap into it - and that's ok. I'd just rather test these if I am able to and would love to know how to do it if possible.
const NodeHelper = require("node_helper");
module.exports = NodeHelper.create({
start: function() {
//do stuff
};
});
Edit: NodeHelper returns a function that appears to be "extended":
NodeHelper.create = function(moduleDefinition) {
return NodeHelper.extend(moduleDefinition);
};
Looking closer at the linked code, it uses Resig's class.js so you probably need to create an instance to call the prototype methods, ie
const YourNodeHelper = require('path/to/your/module')
const yourNodeHelper = new YourNodeHelper() // create instance here
yourNodeHelper.start()

What's the recommended way to unit test a single method in a yeoman generator?

The doc for yeoman unit testing seems to be oriented around integration testing, namely running the entire generator and then examining the side effects produced i.e. for the existence of certain files. For this you can use helpers.run().
This is all fine and well, but I also want to be able to unit test a single method (or "priority") and test internal states of the generator i.e. internal vars. I have been able to do this before by using createGenerator like so:
subAngularGenerator = helpers.createGenerator('webvr-decorator:sub-angular', [
path.join(__dirname, '../generators/sub-angular')
],
null,
{'artifacts': artifacts, appName: APP_NAME, userNames: userNames,
});
This has no RunContext, but I can usually add enough things to the structure so that it will run. For instance:
// mixin common class
_.extend(subAngularGenerator.prototype, require('../lib/common.js'));
// we need to do this to properly feed in options and args
subAngularGenerator.initializing();
// override the artifacts hash
subAngularGenerator.artifacts = artifacts;
// call method
subAngularGenerator._injectDependencies(fp, 'controller', ['service1', 'service2']);
Which allows me to test internal state:
var fileContents = subAngularGenerator.fs.read(fp);
var regex = /\('MainCtrl', function \(\$scope, service1, service2\)/m;
assert(regex.test(fileContents));
This works fine as long as the method is basic javascript, like for/next loops and such. If the method make use of any 'this' variables, like this.async(), I get 'this.async' is not a function.
initialPrompt: function () {
var prompts = [];
var done = this.async(); //if this weren't needed my ut would work
...
I can manually add a dummy this.async, but then I go down the rabbit's hole with other errors, like 'no store available':
AssertionError: A store parameter is required
at Object.promptSuggestion.prefillQuestions (node_modules/yeoman-generator/lib/util/prompt-suggestion.js:98:3)
at RunContext.Base.prompt (node_modules/yeoman-generator/lib/base.js:218:32)
at RunContext.module.exports.AppBase.extend.prompting.initialPrompt (generators/app/index.js:147:12)
at Context.<anonymous> (test/test-app.js:158:42)
I tried to create a runContext and then add my generator to that:
var helpers = require('yeoman-generator').test;
// p.s. is there a better way to get RunContext?
var RunContext = require('../node_modules/yeoman-generator/lib/test/run-context');
before(function (done) {
appGenerator = helpers.createGenerator('webvr-decorator:app', [
path.join(__dirname, '../generators/app')
],
null,
appName: APP_NAME, userNames: userNames,
{});
app = new RunContext(appGenerator); //add generator to runContext
});
app.Generator.prompting.initialPrompt(); //gets async not defined
But this gets the same problem.
My theory is the problem has to with 'this' contexts. Normally the method runs with the 'this' context of the entire generator (which has a this.async etc), but when I run the method individually, the 'this' context is just that of the method/function itself (which has no async in its context). If this is true, then it's really more of a javascript question, and not a yeoman one.
It seems like there should be an easy way to unit test individual methods that depend on the generator context such as calls to this.async. I referred to generator-node as an example of best practices, but it only appears to be doing integration testing.
Does anyone have any better ideas, or do I need to just keep futzing around with JavaScript techniques?
Many Thanks.
I was able to get it to work, but it's a total hack. I was able to decorate a RunContext with the necessary artifacts, and then using apply, I put my generator in the context of the RunContext:
var appGenerator;
var app;
before(function (done) {
// create a generator
appGenerator = helpers.createGenerator('webvr-decorator:app', [
path.join(__dirname, '../generators/app')
],
null,
appName: APP_NAME, userNames: userNames,
{}
);
// get a RunContext
app = new RunContext(appGenerator);
// the following did *not* work -- prompts were not auto-answered
app.withPrompts({'continue': true, 'artifactsToRename': {'mainCtrl' : 'main'}});
//add the following functions and hashes from the generator to the RunContext
app.prompt = appGenerator.prompt;
app._globalConfig = appGenerator._globalConfig;
app.env = appGenerator.env;
// the following two lines are specific to my app only
app.globals = {};
app.globals.MAIN_CTRL = 'main';
done();
});
it('prompting works', function () {
// Run the generator in the context of RunContext by using js 'call'
appGenerator.prompting.initialPrompt.call(app);
}
I no longer get any 'missing functions' messages, but unfortunately the prompts are not being automatically provided by the unit test, so the method stops waiting for something to feed the prompts.
The big "secret" was to call with apply which you can use to override the default this context. I put the generator in the context of the RunContext, which verifies my theory that the problem is about being in the improper context.
I assume there's a much better way to do this and that I'm totally missing something. But I thought I'd at least document what I had to do to get it to work. In the end, I moved the variable initialization code from the 'prompting'method, into the 'initializing' method, and since my 'intializing' method has no Yeoman runtime dependencies, I was able to use a simple generator without a RunContext. But that was just fortuitous in this case. In the general case, I would still like to find out the proper way to invoke a single method.

Organizing code of a midly sized javascript client side app for testing

I'm building a midly size app using backbone and its friends jquery and underscore. My plan is to use QunitJS to create unittests.
I already have a Proof-Of-Concept of the app, so I basicly have a good grasp of how the code should look like without having to test it. It looks like that:
(function() {
// prepare some model
var Query = BackboneModel.extend({});
query = new Query();
// register some events
$('body.something').click(function() {
query.set('key', 'value');
# ...
});
// create some backbone view
var QueryView = Backbone.View.extend({...})
// init backbone view
query.view = new QueryView();
// add some plumbing here ...
// and so on...
})();
Otherwise said:
I wrap the module inside a function to avoid pollution
Class declaration and class use is interleaved
event registration is interleaved with the rest of the code
variables global to the module are reused inside the module
Now I need to test that. The problem is, I think, mainly about event registration and plumbing.
My plan is to wrap the code in functions and export every function and objects I want to test. The code will look like this:
var app = (function() {
var app = {}
// prepare some model
var Query = BackboneModel.extend({});
app.query = new Query();
// register some events
app.registerEvent = function() {
$('body.something').click(function() {
query.set('key', 'value');
# ...
});
};
app.registerEvent(); // XXX: call immediatly
// create some backbone view
app.QueryView = Backbone.View.extend({...})
// init backbone view
app.query.view = new QueryView();
// add some plumbing here ...
// wrapped in function with correct arguments and call it immediatly
// and so on...
// ...
return app;
})();
This is the first time I need to write tests in javascript for this kind of application so I'm wondering whether my approach to make the code testable is correct or can be improved. For instance, It seems silly to me to wrap the registration of events in function without arguments and call them immediatly.
Is there a javascript way to do this?
So I found an excellent way to test private functions while keeping my production code clean. I am not sure if you are using any build system like Grunt or Gulp, but if you are open to it you could do something like this:
//has a dependency of 'test' causing it to run testing suite first
gulp.task('js', ['test'], function() {
return gulp.src(source)
.pipe(plumber())
//create sourcemaps so console errors point to original file
.pipe(sourcemaps.init())
//strip out any code between comments
.pipe(stripCode({
start_comment: 'start-test',
end_comment: 'end-test'
}))
//combine all separatefiles into one
.pipe(concatenate('mimic.min.js'))
//minify and mangle
.pipe(uglify())
.pipe(sourcemaps.write('maps'))
.pipe(gulp.dest('dist/js'));
});
And the file could look like:
var app = (function () {
function somePrivateFunction () {}
function someotherPrivateFunction () {}
var app = {
publicFunction: function(){}
publicVar: publicVar
}
/* start-test */
app.test = {
testableFunction: somePrivateFunction
}
/* end-test */
}());
Everything between the test comments gets stripped out after the tests are run so the production code is clean. Grunt has a version of this and I assume any automated build system can do the same. You can even set a watch task so that it runs the tests on every save. Otherwise you'll have to manually remove the exported test object before deployment.
In the case of Backbone just attach a test object to the module and reference that object in the tests. Or if you really want to separate it, set the object in the global scope. window.testObject = { //list of objects and methods to test... }; and strip that code out before deployment.
Basically what I do is avoid any calls in the library I want to test. So everything is wrapped in a function and exported.
Then I have two other files one is main.js where I do the plumbing and should be tested using integration tests with selenium and tests.js which does unit testing.

NodeJS - How to reference function in one require() file from another require() file?

This is my second weekend playing with Node, so this is a bit newbie.
I have a js file full of common utilities that provide stuff that JavaScript doesn't. Severely clipped, it looks like this:
module.exports = {
Round: function(num, dec) {
return Math.round(num * Math.pow(10,dec)) / Math.pow(10,dec);
}
};
Many other custom code modules - also included via require() statements - need to call the utility functions. They make calls like this:
module.exports = {
Init: function(pie) {
// does lots of other stuff, but now needs to round a number
// using the custom rounding fn provided in the common util code
console.log(util.Round(pie, 2)); // ReferenceError: util is not defined
}
};
The node.js file that is actually run is very simple (well, for this example). It just require()'s in the code and kicks off the custom code's Init() fn, like this:
var util = require("./utilities.js");
var customCode = require("./programCode.js");
customCode.Init(Math.PI);
Well, this doesn't work, I get a "ReferenceError: util is not defined" coming from the customCode. I know everything in each required file is "private" and this is why the error is occuring, but I also know that the variable holding the utility code object has GOT to be stored somewhere, perhaps hanging off of global?
I searched through global but didn't see any reference to utils in there. I was thinking of using something like global.utils.Round in the custom code.
So the question is, given that the utility code could be referred to as anything really (var u, util, or utility), how in heck can I organize this so that other code modules can see these utilities?
There are at least two ways to solve this:
If you need something from another module in a file, just require it. That's the easy one.
Provide something which actually builds the module for you. I will explain this in a second.
However, your current approach won't work as the node.js module system doesn't provide globals as you might expect them from other languages. Except for the things exported with module.exports you get nothing from the required module, and the required module doesn't know anything of the requiree's environment.
Just require it
To avoid the gap mentioned above, you need to require the other module beforehand:
// -- file: ./programCode.js
var util = require(...);
module.exports = {
Init: function(pie) {
console.log(util.Round(pie, 2));
}
};
requires are cached, so don't think too much about performance at this point.
Keep it flexible
In this case you don't directly export the contents of your module. Instead, you provide a constructor that will create the actual content. This enables you to give some additional arguments, for example another version of your utility library:
// -- file: ./programCode.js
module.exports = {
create: function(util){
return {
Init: function(pie) {
console.log(util.Round(pie, 2));
}
}
}
};
// --- other file
var util = require(...);
var myModule = require('./module').create(util);
As you can see this will create a new object when you call create. As such it will consume more memory as the first approach. Thus I recommend you to just require() things.

Faking/Mocking a Javascript object for Unit Tests

Quite a large portion of my work day to day involves working with Dynamics CRM and writing JS to extend the functionality on the forms.
Most clientside interaction in Dynamics involves using an object provided for you when the form loads, which is just Xrm. So you might have something like:
function OnLoad() {
Xrm.Page.getAttribute('name').setValue('Stackoverflow!');
var x = Xrm.Page.getAttribute('name').getValue();
}
I tend to write a wrapper for the Xrm object, mainly because it is a lot easier than remembering some of the chaining and end up with something like:
function WrappedXrm(realXrm) {
var xrm = realXrm;
this.getValue(name) {
return xrm.getAttribute(name).getValue();
}
}
//and then use it as so
var myXrm = new FakeXrm(Xrm);
var myXrmValue = myXrm.getValue('Name');
I am trying out QUnit and wondering how would I go about unit testing in this scenario?
Obviously the example above is a single line, it might not be worth testing it. But assume there was some business logic there that I wanted to test.
The only way I can see is doing some set up before each test along the lines of
var fakeXrm = {};
fakeXrm.Page = {};
fakeXrm.Page.getAttribute = function(name) {
var tempAttr = {};
tempAttr.getValue = function() {
return 'A fake value';
}
}
And then testing on 'A fake value' being returned, but this doesn't 'feel' right to me at all.
Where am I going wrong?
Using Mocks
So in this case, you want to create an instance of WrappedXrm, and pass it an object that mocks the Xrm from your lib ; you need a mock of Xrm.
A first alternative is to write it like you did (which is perfectly valid, if you know what the interface of Xrm is.)
Some libraries like sinon.js or "spies" in the jasmine framework can help you write code like ;
create a 'mock' Xrm, to configure what it should return
create an instance of WrappedXrm with this mock
call the getValue method of WrappedXrm
check that some method was called on the mock
But in the case of javascript, simply created a object that has just the right properties might be okay.
Note that your tests would break if the structure of the "real" Xrm object changes ; that might be what bother's you, but that's always the risk with mocks...
Using the real implementation
If you don't want to test against a mock (which might make sense in case of a wrapper), then maybe you can write the mimimal code that would create an actual Xrm object in your qunit html page (Maybe hardcoding markup ? I don't know the library, so...)
Hoping this helps.

Categories