Patch/mock function inside another function for testing in javascript - javascript

Basically I'd like to patch functions that another function calls so that I can confirm they've been called. Is this possible in js? (I'm using Mocha/Chai/Sinon for testing).
A simplified version of what I'd like to do:
// in render.js
export helper = function() {}
export default = function() {
helper()
}
// in the render-test.js
import render, { helper } from 'render'
// what I'd like to to:
helper = sinon.spy()
render()
assert(helper.called, true)

It's possible, but it does require some rewriting, most notably the way you're calling helper in render.js.
Here's an example that works:
// render.js
export function helper() {}
export default function() {
exports.helper()
}
// render-test.js
import render, * as renderFuncs from 'render'
...
sinon.spy(renderFuncs, 'helper');
render()
assert(renderFuncs.helper.called)
The reason you need to call exports.helper() instead of just helper() is because the latter is a local reference to the helper function, which you can't access from the test file.
The exports object is accessible from the test file (it's given the name renderFuncs), so Sinon can change the reference to helper and wrap it with a spy.

Related

Get exported singleton instance from string in javascript

I have an ExampleView class which should be singleton. Inside this class I have method render which I would like to call from another js file via object name 'ExampleView'.
Here is the code:
import View from './view';
class ExampleView extends View {
render() {
return `<span>Test</span>`;
}
}
export default new ExampleView(); //<- exporting as singleton and this object I try to get via it's name
First I should do is retrieve somehow exported singleton object ExampleView from name 'ExampleView' and then call method render.
class Test {
getInstanceOfModuleByName(name) {
//window[name].render();
}
}
The problem is that I cannot find any way to get ExampleView instance from name, but my ExampleView class needs to be a singleton.
I know I can do this in following way and then call it by window['ExampleView'].render():
ExampleView = {
render: function() {
return `<span>Test</span>`;
}
}
But I really need to implement it with modulable ES6.
Can you explain me what I should do?
Populating global scope is not the ES6 way. I think you can do it like this to your export
//
// Class definition
//
window.ExampleView = new ExampleView();
export default window.ExampleView;
You just import the instance
import whateverYouWantToCallYourViewInstance from './ExampleView'
// snip
return whateverYouWantToCallYourViewInstance.render();
It will be the same object.
Worth noting: Singletons are evil. Using the ES module system to pass instances around is a bit of abuse.

Why does mutating a module update the reference if calling that module from another module, but not if calling from itself?

This question pertains to testing javascript and mocking functions.
Say I have a module that looks like this:
export function alpha(n) {
return `${n}${beta(n)}${n}`;
}
export function beta(n) {
return new Array(n).fill(0).map(() => ".").join("");
}
Then I can't test it the following way:
import * as indexModule from "./index";
//Not what we want to do, because we want to mock the functionality of beta
describe("alpha, large test", () => {
it("alpha(1) returns '1.1'", () => {
expect(indexModule.alpha(1)).toEqual("1.1"); //PASS
});
it("alpha(3) returns '3...3'", () => {
expect(indexModule.alpha(3)).toEqual("3...3"); //PASS
});
});
//Simple atomic test
describe("beta", () => {
it("beta(3) returns '...'", () => {
expect(indexModule.beta(3)).toEqual("..."); //FAIL: received: 'x'
});
});
//Here we are trying to mutate the beta function to mock its functionality
describe("alpha", () => {
indexModule.beta = (n) => "x";
it("works", () => {
expect(indexModule.alpha(3)).toEqual("3x3"); //FAIL, recieved: '3...3'
});
});
However, if split the module into two:
alpha.js
import { beta } from "./beta";
export function alpha(n) {
return `${n}${beta(n)}${n}`;
}
beta.js
export function beta(n) {
return new Array(n).fill(0).map(() => ".").join("");
}
Then I can mutate the beta module, and alpha knows about it:
import { alpha } from "./alpha";
import * as betaModule from "./beta";
describe("alpha", () => {
betaModule.beta = (n) => "x";
it("works", () => {
expect(alpha(3)).toEqual("3x3"); //PASS
});
});
Why is this the case? I'm looking for a technically specific answer.
I have a Github branch with this code here, see the mutateModule and singleFunctionPerModuleAndMutate folders.
As an additional question - in this example I am mutating the module by directly reassigning properties. Am I right in understanding that using jest mock functionality is going to be essentially doing the same thing?
ie. If the reason that the first example doesn't work but the second doesn't is due to the mutation, then it necceserily means that using the jest module mocking functions is similarly not going to work.
As far as I know - there is not way to mock a single function in a module, while testing that module, as this jest github issues talks about. What I'm wanting to know - is why this is.
Why does mutating a module update the reference if calling that module from another module, but not if calling from itself?
"In ES6, imports are live read-only views on exported values".
When you import an ES6 module you essentially get a live view of what is exported by that module.
The live view can be mutated, and any code that imports a live view of the module exports will see the mutation.
That is why your test works when alpha and beta are in two different modules. The test modifies the live view of the beta module, and since the alpha module uses the live view of the beta module, it automatically uses the mocked function instead of the original.
On the other hand, in the code above alpha and beta are in the same module and alpha calls beta directly. alpha does not use the live view of the module so when the test modifies the live view of the module it has no effect.
As an additional question - in this example I am mutating the module by directly reassigning properties. Am I right in understanding that using jest mock functionality is going to be essentially doing the same thing?
There are a few ways to mock things using Jest.
One of the ways is using jest.spyOn which accepts an object and a method name and replaces the method on the object with a spy that calls the original method.
A common way to use jest.spyOn is to pass it the live view of an ES6 module as the object which mutates the live view of the module.
So yes, mocking by passing the live view of an ES6 module to something like jest.spyOn (or spyOn from Jasmine, or sinon.spy from Sinon, etc.) mutates the live view of the module in essentially the same way as directly mutating the live view of the module like you are doing in the code above.
As far as I know - there is not way to mock a single function in a module, while testing that module, as this jest github issues talks about. What I'm wanting to know - is why this is.
Actually, it is possible.
"ES6 modules support cyclic dependencies automatically" which means that the live view of a module can be imported into the module itself.
As long as alpha calls beta using the live view of the module that beta is defined in, then beta can be mocked during the test. This works even if they are defined in the same module:
import * as indexModule from './index' // import the live view of the module
export function alpha(n) {
return `${n}${indexModule.beta(n)}${n}`; // call beta using the live view of the module
}
export function beta(n) {
return new Array(n).fill(0).map(() => ".").join("");
}
What I find interesting is that none of your code would work in a browser.
Module ("./some/path/to/file.js"):
const x = () => "x"
const y = () => "y"
export { x, y }
You cannot modify a named import as they are constants:
import { x } from "./some/path/to/file.js"
x = () => {} //Assignment to constant variable.
You also cannot assign to a readonly property of a namespace import.
import * as stuff from "./some/path/to/file.js"
stuff.y = () => {} //Cannot assign to read only property 'y' of...
Here's a codepen which also shows why indexModule.alpha !== alpha from the module: https://codepen.io/bluewater86/pen/QYwMPa
You are using the module to encapsulate your two functions but for the reasons above, this is a bad idea. You really need to encapsulate those functions in a class so that you can mock them appropriately.
//alphaBeta.js
export const beta = n => new Array(n).fill(0).map(() => ".").join("");
export default class alphaBeta {
static get beta() { return beta }
beta(n) {
beta(n)
}
alpha(n) {
return `${n}${this.beta(n)}${n}`;
}
}
export { alphaBeta }
And finally by moving to default/named imports instead of namespace imports you will have no need to use the cyclic dependency hack. Using default/named imports means that you will be importing the same in-memory view of the exports that the module exported. i.e. importer.beta === exporter.beta
import alphaBetaDefault, { alphaBeta, beta } from "./alphaBeta.js"
alphaBeta.prototype.beta = (n) => "x";
describe("alphaBeta", () => {
it("Imported function === exported function", () => {
expect(alphaBeta.beta).toEqual(beta); //PASS
});
const alphaBetaObject = new alphaBeta
it("Has been mocked", () => {
expect(alphaBetaObject.alpha(3)).toEqual("3x3");
});
alphaBeta.prototype.beta = (n) => "z";
it("Is still connected to its prototype", () => {
expect(alphaBetaObject.alpha(3)).toEqual("3z3");
});
const secondObject = new alphaBetaDefault
it("Will still be mocked for all imports of that module", () => {
expect(secondObject.alpha(3)).toEqual("3z3");
});
});

React - binding `this` to an imported function

In my React app, I have a handful of functions that I'd like to be able to access across a few similar components... however, I want to bind this to the shared functions so that they can do things like update the component state, etc... however, it seems that importing the functions and then trying to bind this in the 'typical' React manner does not work.
Here's an illustration of what I'd like to accomplish - in this case, clicking the rendered button would call the function from the imported shared function file and update the component state:
//shared_functions.js
const sharedFunctions = {
testFunction = () => {
this.setState({functionWasRun: true})
}
}
//MyComponent.jsx
import React, { Component } from 'react';
import sharedFunctions from '../static/scripts/shared_functions.js';
let { testFunction } = sharedFunctions;
class MyComponent extends Component {
constructor(props){
super(props);
this.testFunction = this.testFunction.bind(this)
this.state = {
functionWasRun: false
}
}
render(){
return(
<div>
<button onClick={this.testFunction}>Click</button>
</div>
)
}
}
Trying to run this code as is will return an error like:
Uncaught (in promise) TypeError: Cannot read property 'bind' of undefined
and I get what that's all about... but what I'd like to know is: is it possible to bind this to an imported function?
I'm starting to get a lot of similar-looking functions popping up throughout my app and I'd love to simplify things by abstracting them into a shared script, but I'm not sure how to achieve the typical this binding that's needed to achieve state-setting.
The following line is not trying to bind the imported testFunction but rather a method testFunction of <MyComponent>
To bind the imported function, refer to it directly, as follows:
this.testFunction = testFunction.bind(this);
// Notice how: ^--- there is no longer a this here
NB: You're example tries to use bind on an arrow function You cannot bind a new context to an arrow function. The this context of an arrow function will always be set to the location
were it is defined. You can get around this by declaring
testFunction using a regular function declaration:
const sharedFunctions = {
function testFunction(){
this.setState({functionWasRun: true})
}
}
I used it in the following way:
In constructor:
this.handleChange = handleChange.bind(this);
In the imported file (be careful, no arrow):
export const handleChange = function handleChange(event)
{
const { name, value } = event.target;
this.setState({
[name]: object
});
};
import ToastTimeout from 'toastTimout'
ToastTimeout.bind(this)('message to popup')
I was able to get the context of this in a simple service with a setTimeout function that changed the variable on this context
sorry for the sudo code

es6 how to export native object

I want to extend js native object with a new function then export that function, I tried something like this
export Object.prototype.renameProperty = function () {
console.log('do stuff')
}
I got unexpected token error. What the right way to do this?
The whole point of using modules in ES6+ is that you don't need to modify the prototypes of objects you don't own. But if you must write it this way, you'll need to either just declare the code in the file without an export:
prototype.js
Object.prototype.renameProperty = function () {
console.log('do stuff')
}
And import it like so:
import "./prototype.js";
Or if you really need to both add it to the prototype and export it, then:
prototype.js
export const renameProperty = Object.prototype.renameProperty = function () {
console.log('do stuff')
};
And import it like so:
import {renameProperty} from "./prototype.js";
({}).renameProperty(); // do stuff
renameProperty.bind({})(); // do stuff

Stubbing class field-functions in ES7

In my test suite, how can I stub a class' property, which is a function*? With normal methods it's easy using Object.getOwnPropertyNames(component.prototype) and monkey patching each found method, but after a long time of struggle I haven't found any way to extract the functions created by assigning to class' fields.
My testing stack consists of Jest with Jasmine2 and babel.
The problem with transpiling is that the arrow-function-properties are (as expected, of course) assigned to instance of the output transpiled "class" (function actually, of course). So I don't see any way of stubbing them other then instantiating this object, am I right? Here is the example of input es7 code and the babel's output. However I don't particularly like this solution, seems very hacky. The other drawback of this solution is that I don't get to directly instantiate the component's class.
(*) The background of this question is unit testing React components written in es7-like classes with arrow functions assigned to class' properties for the purpose of auto binding.
I was having the same problem, when writing unit tests for a project I'm working, and I think I got a good pattern to solve it. Hopefully it helps:
Context
Here is an example of a React component that has a method handleClick defined using the fat arrow notation.
import React, { Component } from 'react';
class Foo extends Component {
componentWillMount() {
this.handleClick();
}
handleClick = (evt) => {
// code to handle click event...
}
render() {
return (
<a href="#" onClick={this.handleClick}>some foo link</a>
);
}
}
Problem
As described in this link Babel will transpile the code so that the handleClick method is only available after instantiation (check lines 31 to 33 of the generated constructor function)
The problem here is that sometimes you need to have access to methods defined using the fat arrow notation before instantiating the class.
Lets say for example that you are writing unit tests for the componentWillMount class method and you want to stub the handleClick so that you only test the desired unit. But now you have a problem, since you can only have access to handleClick after instantiation and componentWillMount method will be called automatically by React as part of its instantiation lifecycle.
Solution
Here is how I can apply a simple pattern to solve problems like this:
import React from 'react';
import { mount } from 'enzyme';
import { expect } from 'chai';
import sinon from 'sinon';
import Foo from './foo';
describe('Foo', () => {
describe('componentWillMount method', () => {
const handleClickStub = sinon.stub();
class FooWrapper extends Foo {
constructor(props) {
super(props);
this.handleClick = handleClickStub;
}
}
it('should register a click event listener to the externalElement property', () => {
handleClickStub.reset();
mount(<FooWrapper />);
expect(handleClickStub.calledOnce).to.be.true;
});
});
});
Explanation
I've wrapped the original Foo component into a FooWrapper where on its constructor after initializing the original component I replace the original handleClick method with a stubbed version allowing me to property test my componentWillMount class.
Due to the way babel transpiles the arrow function syntax on class methods via transform-class-properties, the class method is no longer bound on the prototype, but rather the instance.
Using Jest 19's built-in assertion and .spyOn methods, this was my solution:
import React from 'react';
import { shallow } from 'enzyme';
describe('MyComponent', () => {
it('should spy properly', () => {
const wrapper = shallow(<Component />);
const wrapperInstance = wrapper.instance();
const spy = jest.spyOn(wrapperInstance, 'functionName');
wrapperInstance.functionName();
expect(spy).toHaveBeenCalledTimes(1);
})
});

Categories

Resources