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:
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>
);
}
}
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.
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;
});
});
});
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.