How to rewire only some object parameters during unit testing? - javascript

I have one module that contains an object. I would like to rewire only some of the object's parameters. Let's say my module 'module.js' looks like this:
var obj = {
param_A: 'valueA',
param_B: 'valueB',
param_C: 'valueC'
}
And in the test file I import the module and use rewire.
var rewire = require('rewire');
var module = rewire('../module.js');
describe('Unit-Test', function () {
beforeEach(function () {
module.__set__({
'obj': { param_B: 'newValueB' }
});
});
...
});
Now I have overwritten the entire object and the parameters A and C do not exist anymore. How can keep them? The only thing that comes to my mind is something like this
module.__set__({
'obj': {
param_A: module.obj.param_A,
param_B: 'newValueB',
param_C: module.obj.param_C
}
}
But I feel like there must be a better way.

Can I suggest that you take a look at the 'with' function in require. github/jhnns/rewire
myModule.__with__({
port: 3000
})(function () {
// within this function port is 3000
});
// now port is the previous value again

Related

How to get js function into webWorker via importScripts

I have a worker.js file:
self.importScripts('/static/utils/utils.js')
onmessage = (e) => {
let a = e.data[0]
let b = e.data[1]
let c = func1(a,b)
postMessage(c)
}
The utils.js file looks something like this:
module.exports = {
func1: function(a,b){
return a+b
}
I keep getting error:
Uncaught ReferenceError: module is not defined
at utils.js:1
Obviously require, and import and any other server side imports aren't working but I'm not sure why it's having a problem with my importScripts - https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts
The correct solution is to pack your worker with webpack. If you don't want to do that, read below.
I usually write myself a polyfill for node require:
// This will not work in normal UI thread
// None of this should make it into production
function require(moduleName) {
self.module = { exports: null };
// Nasty sttuff right here, probably should throw error instead
if (moduleName == "fs")
return null;
// This part is especially unprofessional
if (!moduleName.endsWith(".js"))
moduleName += ".js";
importScripts(moduleName);
return self.module.exports;
}
This makes use of the fact that importScripts is synchronous. Note that this will still cause errors if you try to load native node modules (eg. fs) or if other module properties are used.
Try this utils.js:
(function () {
self.func1 = function (a, b) {
return a + b
}
}());
Try to do this:
//inside worker.js
self.addEventListener("message",(event)=>{
importScripts("module.js")
utils.print1()
utils.print2()
})
//inside module.js
//setting a variable in global scope, allows worker.js to use it.
var utils = {
print1(){
console.log("This is a content from a module.")
},
print2(){
console.log("This is a another content from a module.")
}
}

Using external class during client side Mocha unit testing

I am running unit tests on a javascript class using Mocha using the follow methodology, firstly the test:
var base = require('../moduleone.js');
describe("some test", function() {
it("description", function() {
var check = base.toBeTested(dummyValue)
//test is here ...
});
});
the moduleone.js containing function to be tested:
function toBeTested(category){
//below I calling an assert function defined in moduletwo
//works fine when running in browser
assert(type(category)=='string','category is string type');
//more code..
return something
module.exports.toBeTested = toBeTested;
moduletwo.js:
function assert(outcome, description) {
//see code.tutsplus.com quick and easy javascript testing with assert
var li = outcome ? 'pass' : 'fail';
if (li == 'fail') {
console.log('FAIL: '+description);
}
else {
console.log('PASS: '+description);
}
}
The issue I have is mocha doesn't know anything about moduletwo and when the moduleone function calles the function in moduletwo mocha throws a ReferenceError: assert is not defined. How can I link all my dependencies so that mocha can see them?
In your moduleone.js be sure that you are requireing moduletwo.js to bring your assert function into scope for moduleone.js. Otherwise, you get a ReferenceError, not for any reasons with mocha, but because moduleone does not have access to assert.
// moduletwo.js
function assert(outcome, description) { /* your functionality */ }
module.exports = assert
// moduleone.js
var assert = require('./moduletwo')
function toBeTested(category) { /* your functionality that uses assert */ }
module.exports.toBeTested = toBeTested
Now, with regards to that tutorial. If you are following it to learn how to make an easy assert module, that is fine. But if you are trying to test some code that does something else, consider using an existing assertion library like chai. For example:
// example.test.js
var expect = require('chai').expect
var isValidCategory = require('./is-valid-category')
describe('isValidCategory(category)', function () {
it('validates string categories', function () {
expect(isValidCategory('A String Category')).to.be.true
})
it('does not validate non-string categories', function () {
expect(isValidCategory(['an', 'array', 'somehow'])).to.be.false
})
})
// is-valid-category.js
module.exports = function isValidCategory(category) {
return typeof category === 'string'
}

Writing multiple functions in AMD javascript module

I am quite new to writing javascript code using AMD. I am stuck at figuring out how to write multiple functions in a file:
define(function(){
return {
and: function(a,b){
return (a&&b);
}
};
}
);
I tried writing another function plus in the following way:
define(function(){
return {
plus: function(a,b){
return (a+b);
}
};
}
);
But when I use grunt for testing, it is not able to detect the function plus
You should place each module in it's own file. At least requireJS (are you using that?) determines the module name by it's file name (without the .js).
So a file sitting in /modules/A.js will have the module name "modules/A".
If you really want to define multiple modules in one file, you can do it in a more explicit way like this:
define("A", [], function () { return ...whatever... });
define("B", [], function () { return ...whatever... });
Edit:
for defining one module with two functions you can use different patterns. For a singleton (i.e. no "Class") I usually do something like this:
define(function () {
var myModule = {
fn1: function () { .... },
fn2: function () { .... }
};
return myModule;
});

Function from require() throws undefined error

As a beginner to NodeJS this might be straigtforward but yet I am unable to figure out where I am going wrong
My home.js file is as follow
module.exports = function (deps) {
var sample = require('../lib/sample'), // My own library
express = require('express'),
router = express.Router();
router.get('/', function (req, res) {
op = sample.parse('hi'); // Error here
res.send(op);
});
return router;
};
Under lib folder, my sample.js code is
module.exports = function () {
function parse(text) {
return 'hello' + text;
}
return {
'sample': {
'parse': parse
}
};
};
But I get an error saying undefined is not a function on the highlighted line. Can anyone let me know what I am missing?
Since you export a function, sample will be a function now. You need to explicitly execute it to get the same object. So, you need to do something like this
var sample = require('../lib/sample')().sample
Now, the require statement returns the function, and we immediately execute it, which returns an object with sample property. Since you are interested in sample property only, we get only the sample property.
If you were planning to hide the implementation of parse from the users, I would suggest doing
function parse(text) {
return 'hello' + text;
}
module.exports = {
'parse': parse
};
Now, you are simply exporting the parse function, in an object and the code which requires this module will be able to use parse function, like you mentioned in the question.
Your module.exports evaluates to a function which when called yields the object containing the parse function you are trying to call, under some nesting. You might try restructuring your sample.js file to look like this:
function parse(text) {
return 'hello' + text;
}
module.exports = {
parse: parse
};
Unless you really need the function wrapping shown in your example. In that case you'll have to unwrap it where you import it, so something like this:
var sample = require('../lib/sample')().sample
Change your exports to:
module.exports = function () {
function parse(text) {
return 'hello' + text;
}
return {
'parse': parse
};
};

mocking window.location.href in Javascript

I have some unit tests for a function that makes use of the window.location.href -- not ideal I would far rather have passed this in but its not possible in the implementation. I'm just wondering if its possible to mock this value without actually causing my test runner page to actually go to the URL.
window.location.href = "http://www.website.com?varName=foo";
expect(actions.paramToVar(test_Data)).toEqual("bar");
I'm using jasmine for my unit testing framework.
The best way to do this is to create a helper function somewhere and then mock that:
var mynamespace = mynamespace || {};
mynamespace.util = (function() {
function getWindowLocationHRef() {
return window.location.href;
}
return {
getWindowLocationHRef: getWindowLocationHRef
}
})();
Now instead of using window.location.href directly in your code simply use this instead. Then you can replace this method whenever you need to return a mocked value:
mynamespace.util.getWindowLocationHRef = function() {
return "http://mockhost/mockingpath"
};
If you want a specific part of the window location such as a query string parameter then create helper methods for that too and keep the parsing out of your main code. Some frameworks such as jasmine have test spies that can not only mock the function to return desired values, but can also verified it was called:
spyOn(mynamespace.util, 'getQueryStringParameterByName').andReturn("desc");
//...
expect(mynamespace.util.getQueryStringParameterByName).toHaveBeenCalledWith("sort");
I would propose two solutions which have already been hinted at in previous posts here:
Create a function around the access, use that in your production code, and stub this with Jasmine in your tests:
var actions = {
getCurrentURL: function () {
return window.location.href;
},
paramToVar: function (testData) {
...
var url = getCurrentURL();
...
}
};
// Test
var urlSpy = spyOn(actions, "getCurrentURL").andReturn("http://my/fake?param");
expect(actions.paramToVar(test_Data)).toEqual("bar");
Use a dependency injection and inject a fake in your test:
var _actions = function (window) {
return {
paramToVar: function (testData) {
...
var url = window.location.href;
...
}
};
};
var actions = _actions(window);
// Test
var fakeWindow = {
location: { href: "http://my/fake?param" }
};
var fakeActions = _actions(fakeWindow);
expect(fakeActions.paramToVar(test_Data)).toEqual("bar");
You need to simulate local context and create your own version of window and window.location objects
var localContext = {
"window":{
location:{
href: "http://www.website.com?varName=foo"
}
}
}
// simulated context
with(localContext){
console.log(window.location.href);
// http://www.website.com?varName=foo
}
//actual context
console.log(window.location.href);
// http://www.actual.page.url/...
If you use with then all variables (including window!) will firstly be looked from the context object and if not present then from the actual context.
Sometimes you may have a library that modifies window.location and you want to allow for it to function normally but also be tested. If this is the case, you can use a closure to pass your desired reference to your library such as this.
/* in mylib.js */
(function(view){
view.location.href = "foo";
}(self || window));
Then in your test, before including your library, you can redefine self globally, and the library will use the mock self as the view.
var self = {
location: { href: location.href }
};
In your library, you can also do something like the following, so you may redefine self at any point in the test:
/* in mylib.js */
var mylib = (function(href) {
function go ( href ) {
var view = self || window;
view.location.href = href;
}
return {go: go}
}());
In most if not all modern browsers, self is already a reference to window by default. In platforms that implement the Worker API, within a Worker self is a reference to the global scope. In node.js both self and window are not defined, so if you want you can also do this:
self || window || global
This may change if node.js really does implement the Worker API.
Below is the approach I have take to mock window.location.href and/or anything else which maybe on a global object.
First, rather than accessing it directly, encapsulate it in a module where the object is kept with a getter and setter. Below is my example. I am using require, but that is not necessary here.
define(["exports"], function(exports){
var win = window;
exports.getWindow = function(){
return win;
};
exports.setWindow = function(x){
win = x;
}
});
Now, where you have normally done in your code something like window.location.href, now you would do something like:
var window = global_window.getWindow();
var hrefString = window.location.href;
Finally the setup is complete and you can test your code by replacing the window object with a fake object you want to be in its place instead.
fakeWindow = {
location: {
href: "http://google.com?x=y"
}
}
w = require("helpers/global_window");
w.setWindow(fakeWindow);
This would change the win variable in the window module. It was originally set to the global window object, but it is not set to the fake window object you put in. So now after you replaced it, the code will get your fake window object and its fake href you had put it.
This works for me:
delete window.location;
window.location = Object.create(window);
window.location.href = 'my-url';
This is similar to cpimhoff's suggestion, but it uses dependency injection in Angular instead. I figured I would add this in case someone else comes here looking for an Angular solution.
In the module, probably the app.module add a window provider like this:
#NgModule({
...
providers: [
{
provide: Window,
useValue: window,
},
],
...
})
Then in your component that makes use of window, inject window in the constructor.
constructor(private window: Window)
Now instead of using window directly, use the component property when making use of window.
this.window.location.href = url
With that in place you can set the provider in Jasmine tests using TestBed.
beforeEach(async () => {
await TestBed.configureTestingModule({
providers: [
{
provide: Window,
useValue: {location: {href: ''}},
},
],
}).compileComponents();
});
IMO, this solution is a small improvement of cburgmer's in that it allows you to replace window.location.href with $window.location.href in the source. Granted I'm using Karma and not Jasmine, but I believe this approach would work with either. And I've added a dependency on sinon.
First a service / singleton:
function setHref(theHref) {
window.location.href = theHref;
}
function getHref(theHref) {
return window.location.href;
}
var $$window = {
location: {
setHref: setHref,
getHref: getHref,
get href() {
return this.getHref();
},
set href(v) {
this.setHref(v);
}
}
};
function windowInjectable() { return $$window; }
Now I can set location.href in code by injecting windowInjectable() as $window like this:
function($window) {
$window.location.href = "http://www.website.com?varName=foo";
}
and mocking it out in a unit test it looks like:
sinon.stub($window.location, 'setHref'); // this prevents the true window.location.href from being hit.
expect($window.location.setHref.args[0][0]).to.contain('varName=foo');
$window.location.setHref.restore();
The getter / setter syntax goes back to IE 9, and is otherwise widely supported according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
Here's my generic solution that requires an extra import in production code, but doesn't require dependency injection or writing individual wrapper functions like getHref().
Basically we toss the window into a separate file and then our prod code imports the window indirectly from that file.
In production, windowProxy === window.
In tests we can mutate the module which exports windowProxy and mock it with a new temporary value.
// windowProxy.js
/*
* This file exists solely as proxied reference to the window object
* so you can mock the window object during unit tests.
*/
export default window;
// prod/someCode.js
import windowProxy from 'path/to/windowProxy.js';
export function changeUrl() {
windowProxy.location.href = 'https://coolsite.com';
}
// tests/someCode.spec.js
import { changeUrl } from '../prod/someCode.js';
import * as windowProxy from '../prod/path/to/windowProxy.js';
describe('changeUrl', () => {
let mockWindow;
beforeEach(() => {
mockWindow = {};
windowProxy.default = myMockWindow;
});
afterEach(() => {
windowProxy.default = window;
});
it('changes the url', () => {
changeUrl();
expect(mockWindow.location.href).toEqual('https://coolsite.com');
});
});
You need to fake window.location.href while being on the same page.
In my case, this snipped worked perfectly:
$window.history.push(null, null, 'http://server/#/YOUR_ROUTE');
$location.$$absUrl = $window.location.href;
$location.replace();
// now, $location.path() will return YOUR_ROUTE even if there's no such route

Categories

Resources