I'm trying to mock an instance which is created in a React JS component.
The instance is a common ECMAScript 2016 class, not a React component. I use Jasmine, React JS TestUtils and babel-rewire for testing.
My React component code looks like this:
import MyHandler from '../../js/MyHandler';
export default class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myHandler = new MyHandler();
}
someComponentMethod() {
this.myHandler.someMethod();
}
render() {
return <div>...</div>;
}
}
My class looks like this:
export default class MyHandler {
someMethod() {
// ...
}
}
My test and what I tried so far:
// gives exception
let myHandler = new MyHandler();
let spy = spyOn(myHandler, "someMethod").and.returnValue(null);
MyComponent.__Rewire__ ("MyHandler", spy);
// also gives exeption
MyComponent.__set__ ("MyHandler", spy);
let component = TestUtils.renderIntoDocument(<MyComponent />);
For mocking other React components I use babel-rewire which works great. But I can't replace the instance by a mock or spy.
I know I could pass the instace into the component as a property (and thus mock it in a test), but I wonder if this is good practise and I'm afraid I'll have the problem in the next coomponent .
Any help appreciated!
You could use the injector loader something along these lines
const MyComponentInjector = require('inject!'MyComponent');
MyComponent = MyComponentInjector({
'../../js/MyHandler': YourMockedClass
}).default
that would provide a MyComponent with YourMockedClass as Mock for your handler
For those who are interested, here is the complete code:
import TestUtils from 'react-addons-test-utils';
import React from 'react';
import inject from 'inject!../js/MyComponent';
it("mocks the call", () => {
class mockClass {
constructor() {
console.log("some constructor fake");
}
someMethod() {
console.log("some method fake");
}
}
let mock = new mockClass();
let MyComponent = inject({
'../../js/MyHandler': mockClass
}).default;
let component = TestUtils.renderIntoDocument(<MyComponent />);
component.someComponentMethod();
});
Related
I have multiple components which all need to do the same thing. (A simple function which maps over their child components and does something to each one). At the moment I am defining this method in each of the components. But I only want to define it once.
I could define it in the top level component and then pass it down as a prop. But that doesn't feel quite right. It is more a library function than a prop. (It seems to me).
What is the correct way of doing this?
Utils.js with latest Javascript ES6 syntax
Create the Utils.js file like this with multiple functions, etc
const someCommonValues = ['common', 'values'];
export const doSomethingWithInput = (theInput) => {
//Do something with the input
return theInput;
};
export const justAnAlert = () => {
alert('hello');
};
Then in your components that you want to use the util functions, import the specific functions that are needed. You don't have to import everything
import {doSomethingWithInput, justAnAlert} from './path/to/Utils.js'
And then use these functions within the component like this:
justAnAlert();
<p>{doSomethingWithInput('hello')}</p>
If you use something like browserify then you can have an external file i.e util.js that exports some utility functions.
var doSomething = function(num) {
return num + 1;
}
exports.doSomething = doSomething;
Then require it as needed
var doSomething = require('./util.js').doSomething;
If you want to manipulate state in helper functions follow this:
Create a Helpers.js file:
export function myFunc(){ return this.state.name; //define it according to your needs }
Import helper function in your component file:
import {myFunc} from 'path-to/Helpers.js'
In your constructor add that helper function to the class
constructor(){ super() this.myFunc = myFunc.bind(this) }
In your render function use it:
`render(){
{this.myFunc()}
}`
Here are some examples on how you can reuse a function (FetchUtil.handleError) in a React component (App).
Solution 1: Using CommonJS module syntax
module.exports = {
handleError: function(response) {
if (!response.ok) throw new Error(response.statusText);
return response;
},
};
Solution 2: Using "createClass" (React v16)
util/FetchUtil.js
const createReactClass = require('create-react-class');
const FetchUtil = createReactClass({
statics: {
handleError: function(response) {
if (!response.ok) throw new Error(response.statusText);
return response;
},
},
render() {
},
});
export default FetchUtil;
Note: If you are using React v15.4 (or below) you need to import createClass as follows:
import React from 'react';
const FetchUtil = React.createClass({});
Source: https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactcreateclass
Component (which reuses FetchUtil)
components/App.jsx
import Categories from './Categories.jsx';
import FetchUtil from '../utils/FetchUtil';
import Grid from 'material-ui/Grid';
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {categories: []};
}
componentWillMount() {
window
.fetch('/rest/service/v1/categories')
.then(FetchUtil.handleError)
.then(response => response.json())
.then(categories => this.setState({...this.state, categories}));
}
render() {
return (
<Grid container={true} spacing={16}>
<Grid item={true} xs={12}>
<Categories categories={this.state.categories} />
</Grid>
</Grid>
);
}
}
export default App;
I'll show two styles below, and you'll want to choose depending on how much the components' logic relate to each other.
Style 1 - Relatively related components can be created with callback references, like this, in ./components/App.js...
<SomeItem
ref={(instance) => {this.childA = instance}}
/>
<SomeOtherItem
ref={(instance) => {this.childB = instance}}
/>
And then you can use shared functions between them like this...
this.childA.investigateComponent(this.childB); // call childA function with childB as arg
this.childB.makeNotesOnComponent(this.childA); // call childB function with childA as arg
Style 2 - Util-type components can be created like this, in ./utils/time.js...
export const getTimeDifference = function (start, end) {
// return difference between start and end
}
And then they can be used like this, in ./components/App.js...
import React from 'react';
import {getTimeDifference} from './utils/time.js';
export default class App extends React.Component {
someFunction() {
console.log(getTimeDifference("19:00:00", "20:00:00"));
}
}
Which to use?
If the logic is relatively-related (they only get used together in the same app), then you should share states between components. But if your logic is distantly-related (i.e., math util, text-formatting util), then you should make and import util class functions.
Another solid option other than creating a util file would be to use a higher order component to create a withComponentMapper() wrapper. This component would take in a component as a parameter and return it back with the componentMapper() function passed down as a prop.
This is considered a good practice in React. You can find out how to do so in detail here.
Sounds like a utility function, in that case why not put it in a separate static utility module?
Otherwise if using a transpiler like Babel you can make use of es7's static methods:
class MyComponent extends React.Component {
static someMethod() { ...
Or else if you are using React.createClass you can use the statics object:
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
}
However I don't advise those options, it doesn't make sense to include a component for a utility method.
Also you shouldn't be passing a method down through all your components as a prop it will tightly couple them and make refactoring more painful. I advise a plain old utility module.
The other option is to use a mixin to extend the class, but I don't recommend that as you can't do it in es6+ (and I don't see the benefit in this case).
Shouldn't you use a Mixin for this ? See https://facebook.github.io/react/docs/reusable-components.html
Although they are falling out of favour see https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
Might be useful
just like this:
const readOnlyActions = ['a','b','c'];
export default class ActionLibraryComponent extends Components {
constructor(props){
...
this.isReadOnlyLibrary = this.isReadOnlyLibrary.bind(this);
...
}
isReadOnlyLibrary(library){
return readOnlyLibrary.includes(library.uuid);
}
}
But, the eslint told me that thismust be used in the class methods;but it is not necessary for me to bind the const value readOnlyLibraryin the Component ActionLibraryComponent,but how can I use it?
The point is that the method isReadOnlyLibrary does not need to be a method of the class. You can create it as a function outside of the component, and it will work just fine.
Like:
const readOnlyActions = ['a','b','c'];
const isReadOnlyLibrary = (library) => readOnlyActions.includes(library.uuid);
export default class ActionLibraryComponent extends Components {
constructor(props){
...
this.isReadOnlyLibrary = isReadOnlyLibrary;
...
}
}
To be fair, you don't even need this function to be in this component, once it does not use anything from it.
Note that as that is no longer a part of the ActionLibraryComponent component does not need the bind.
Let me know if that does not seems clear.
Using javascript ES6 (React), I'm not able to call a simple method of an imported class.
What's wrong with this code?
TypeError: WEBPACK_IMPORTED_MODULE_1__Seed.a.test is not a
function
// App.js
import React from 'react';
import Seed from './Seed';
class App extends React.Component {
constructor(props) {
super(props);
console.log('start1');
Seed.test();
}
render() {
return("ei");
}
}
export default App;
and
// Seed.js
import React from 'react';
class Seed extends React.Component {
constructor(props) {
super(props);
console.log('seed1');
}
test() {
console.log('seed test');
}
};
export default Seed;
There are few options, depending on what you're trying to do
1) If this function is unrelated to an instance of a Seed, then make it static.
class Seed extends React.Component {
static test() {
console.log('seed test');
}
// ...etc
}
Then you can call it the way you're already calling it.
2) If it needs to be tied to a specific instance of a seed, you could new one up and then call it. For example:
const mySeed = new Seed();
mySeed.test();
Given that Seed is a react component this is very likely not what you want to do, since you should let react do the instantiating of components and then interact with it through props
3) Use refs to let react give you a reference to the component. I'll assume you're using react 16 or higher and thus have access to React.createRef
constructor(props) {
super(props);
this.seedRef = React.createRef();
}
componentDidMount() {
this.seedRef.current.test();
}
render() {
return <Seed ref={this.seedRef}/>
}
This is better, but its still questionable that you would want to interact with a component this directly.
4) Use props, don't call it directly. Exactly how to do this depends what you're trying to do, but suppose you want to only call the method if some condition is true. Then you could pass a prop in to the Seed, and the seed calls the method itself.
// in App:
render() {
render <Seed shouldDoStuff={true} />
}
// In seed:
constructor(props) {
super(props);
if (props.shouldDoStuff) {
this.test();
}
}
You can do that with declare test as static like this
class Seed extends React.Component {
static test() {
console.log('seed test');
}
constructor(props) {
super(props);
console.log('seed1');
}
};
if you want call test in Seed component use Seed.test()
You cannot access a class' method like that since it's not static.
You would need to have App render with a <Seed /> and get a ref to that component.
// App.js
import React from 'react';
import Seed from './Seed';
class App extends React.Component {
constructor(props) {
super(props);
console.log('start1');
this.seedRef = React.createRef();
}
componentDidMount() {
// seedRef is the Seed instance
this.seedRef.current.test();
}
render() {
return(<Seed ref={this.seedRef} />);
}
}
export default App;
I have a module which is not a react component (let's call it A) but its variables get displayed by react. Now A has to call the forceUpdate function of App.js. But I don't want to pass always App.js as a property when I create a new object of A.
I've created following:
App.js:
let forceUpdateCaller;
export default class App extends React.Component {
static forceUpdate = () => {
if (forceUpdateCaller) {
forceUpdateCaller();
}
}
forceUpdateCaller = () => {
this.forceUpdate();
}
constructor(props) {
super(props);
forceUpdateCaller = this.forceUpdateCaller;
}
...
}
A:
import App from "../App";
export default class A extends ...{
constructor() {
super();
}
updateContent = () => {
...
App.forceUpdate();
}
}
But I'm sure, there's a better, cleaner way. Do you have any ideas?
Found the best way:
export let ref;
class App extend React.Component{
constructor(props){
super(props);
ref = this;
}
}
Now I have full access to the App object when I import {ref}. :)
Im very new to react/jest. Im trying to test a very simple react component that gets data from the server and renders the response. My component looks like the below:
export default class myComponent extends Component {
constructor(props) {
super(props);
}
async componentDidMount() {
try {
let response = await axios.get(`server/url/endpoint`);
this._processSuccess(response.data);
} catch(e) {
this._processFail(e);
}
}
_processSuccess(response) {
this.setState({pageTitle: response.data.title, text: response.data.text});
}
render() {
return (
<div className="title">{this.state.pageTitle}</div>
);
}
}
Now I want to test this class. While I test:
I want to make sure componentDidMount() was not called
I want to pass test data to _processSuccess
Finally check the if the rendered output contains a div with class title that has the inner text same as what I supplied as response.data/pageTitle
I tried something like the below:
import React from 'react'
import MyComponent from './MyComponent'
import renderer from 'react-test-renderer'
import { shallow, mount } from 'enzyme'
describe('MyComponent', () => {
it('should display proper title', () => {
const c = shallow(<MyComponent />);
c._processSuccess(
{data:{pageTitle:'siteName', test:'text'}}
);
// couldn't go further as Im getting error from the above line
});
});
But, Im getting MyComponent._processSuccess is not a function error. What would be the proper way to do that.
shallow() returns an Enzyme wrapper with some utils method to test the rendered component. It does not return the component instance. That's why you get the error when calling c._processSucces(). To access the component you can use the .instance() method on the wrapper, so the following should work:
const c = shallow(<MyComponent />);
c.instance()._processSuccess(
{data:{pageTitle:'siteName', test:'text'}}
);
In order to avoid that component's componentDidMount() get called, you can try settings disableLifecycleMethods on the shallow renderer, but I'm not sure about that because here Enzyme's documentation is not 100% clear:
const c = shallow(<MyComponent />, {
disableLifecycleMethods: true
});
Finally, you can check if the output contains the expected <div>, using Enzyme's contains() and one of Jest assertion methods:
expect(c.contains(<div className="title" />)).toBe(true);