How do you mock a class function in componentDidMount - javascript

class myClass extends React.Component {
async componentDidMount(){
const xyz = helperclass.queryParameters('parameter')
// how can I mock xyz to help me change the value to test the conditional below
if(xyz){
// do something here
}
}
}
have edited the code above, now I want to test the const xyz to the value of the helper function by mocking it out and then returning different things to test if the stuff inside the conditional was done

Since, myFunctionToMock is prototype method, it can be mocked as
jest.spyOn(MyClass.prototype, 'myFunctionToMock').mockImplementation(...);
before class instantiation.
Another option in Enzyme is to disable hooks with disableLifecycleMethods option, mock a method on wrapper.instance() and call componentDidMount manually.
If a method doesn't belong to this, it can be mocked on an object it belongs to:
jest.spyOn( helperclass, 'queryParameters').mockImplementation(...);
Again, this should be done before class instantiation, or disableLifecycleMethods needs to be used.

Related

Test method called in mounted hook Vue class component typescript

I am trying to test that a method is called on mount of Vue component. Fairly new to Vue and Typescript.
export default class App extends Vue {
mounted () {
this.deviceId()
this.ipAddress()
this.channel()
this.show()
this.campaign()
this.adUnit()
}
this approach works but I get a warning:
it('mounted methods are called', async () => {
const deviceId = jest.fn()
wrapper = shallowMount(App, {
methods: {
deviceId
}
})
expect(deviceId).toHaveBeenCalled()
})
The error:
console.error node_modules/#vue/test-utils/dist/vue-test-utils.js:1735
[vue-test-utils]: overwriting methods via the `methods` property is deprecated and will be removed in the next major version. There is no clear migration path for the `methods` property - Vue does not support arbitrarily replacement of methods, nor should VTU. To stub a complex method extract it from the component and test it in isolation. Otherwise, the suggestion is to rethink those tests.
I have tried using jest spyOn, but I cannot find a way to access the method;
const spy = jest.spyOn(App.prototype, 'methodName')
wrapper = shallowMount(App)
expect(spy).toHaveBeenCalled()
Gives the following error:
Cannot spy the deviceId property because it is not a function; undefined given instead
The following also doesn't work:
const spy = jest.spyOn(App.methods, 'methodName')
Error:
Property 'methods' does not exist on type 'VueConstructor<Vue>'.ts(2339)
And the following:
const spy = jest.spyOn(App.prototype.methods, 'deviceId')
Error:
Cannot spyOn on a primitive value; undefined given
I have read in places I may need to define an interface for the component but I am not sure how this works with defining functions inside or if it is necessary?
I've been facing the same issue for a few days, but I've found the way of pointing to the correct method when calling jest.spyOn().
It's a bit tricky but you'll find the methods of your class like this:
const spy = jest.spyOn(App.prototype.constructor.options.methods, 'deviceId');
Note that (even if it might seem obvious, just in case) you'll need to do this before wrapping your component, i.e. like this:
const spy = jest.spyOn(App.prototype.constructor.options.methods, 'deviceId');
wrapper = mount(App, { /* Your options go here */ });
By the way, you don't need to define methods property inside options.
Define your method under the property methods. Only then you can access them from the class.
export default class App extends Vue {
methods: {
deviceId(){
console.log("do your stuff")
}
}
}
See here for more examples for the usage of methods

testing nested component callback in react

given a Foo (root) component that renders Baz nested component, where Baz has a property named onOperationDone which accepts a callback.
class Foo extends React.Component {
constructor(props) {
super(props);
this.onOperationDone = () => {
console.log("do something");
}
}
render() {
return(
<div>
<Baz onOperationDone={this.onOperationDone} />
</div>
)
}
}
what are the steps that needs to be done to make Baz to execute the callback in order to make sure the callback is being invoked (using enzyme or test-utils)?
could you please help me understand how it should be done?
Some things seem a bit weird to me in your code. I'm assuming they are typos when creating the question but I will comment on them nonetheless. If what I'm assuming happens to be wrong, say so and I will edit my answer accordingly.
First of all, your render method is not returning anything. Typically, the JSX should be placed inside a return statement.
Secondly, the onOperationDone method is declared inside the constructor the class. That means every time you create a new instance of the class the method is also created (taking the necessary amount of memory). Instead, I would define the method in the prototype of the class, so it is shared between all instances.
With that in mind, your class would look like (note that I have deleted the constructor since it would only be calling the super and that is done automatically):
class Foo extends React.Component {
onOperationDone() {
console.log("do something");
}
render() {
return (
<div>
<Baz onOperationDone={this.onOperationDone} />
</div>
);
}
}
Now, to test that when the Baz component calls the onOperationDone property the onOperationDone method of Foo is called, I would set an spy on the Foo onOperationDone method to check that it is called. Then, I would search for the Baz element and call its onOperationDone.
With enzyme, you can do:
it('the child calls its parent callback', function() {
jest.spyOn(Foo.prototype, 'onOperationDone');
const wrapper = shallow(<Foo />);
wrapper.find(Baz).prop('onOperationDone')();
expect(Foo.prototype.onOperationDone).toHaveBeenCalledTimes(1);
});
Spying methods of the class instances
If you are trying to spy a method that belongs to the instances of your class (whether it is by defining the method in the constructor, as in your case, or by using class fields), the thing gets a bit trickier.
Let's say you are trying to spy the onOperationDone in your initial code:
export default class Foo extends React.Component {
constructor(props) {
super(props);
this.onOperationDone = () => {
console.log("do something");
};
}
render() {
return (
<div>
<Baz onOperationDone={this.onOperationDone} />
</div>
);
}
}
If you try the same approach from spying the prototype but spying instead the instance method, it will not work:
it('the child calls its parent callback', function() {
const wrapper = shallow(<Foo />);
const instance = wrapper.instance();
jest.spyOn(instance, 'onOperationDone');
wrapper.find(Baz).prop('onOperationDone')();
expect(instance.onOperationDone).toHaveBeenCalledTimes(1);
});
It will fail stating that the spied method was not called (although you will see the log "do something").
This is because when you shallow render the Foo component, a new onOperationDone method is being created and added to the instance, and then the render method is called and the onOperationDone is being assigned as a prop to the Baz component.
Next, you are spying the instance method (with jest.spyOn) but what this does is that it creates a new method that wraps your original onOperationDone in order to track the number of times it has been called and other statistics. The thing is, that the Baz prop has not changed, and it is a reference to the original method, not the wrapped one. So the wrapped method never gets called.
To overcome this, we need to force an update of the component (so that the wrapped onOperationDone gets assigned as prop of Baz component. To do that we have the update method of enzyme's shallow renderer. Unfortunately, it seems that the update method does not always force a re-render.
So a workaround is to call the setProps method to force the update. The final test code should look like:
it('the child calls its parent callback', function() {
const wrapper = shallow(<ChildComponentCallbackInstance />);
const instance = wrapper.instance();
jest.spyOn(instance, 'onOperationDone');
// wrapper.update(); would be the ideal method to call
wrapper.setProps({});
wrapper.find(Baz).prop('onOperationDone')();
expect(instance.onOperationDone).toHaveBeenCalledTimes(1);
});

Should I be binding also inbuilt React methods?

It is a common practice to be bind a user-created method inside a React.Component class.
class App extends React.Component { // simplified example, not actual code
constructor(){
//...
this.logIn=this.logIn.bind(this) // Binding of a method
}
}
Naturally, this is beceause we need to explicitly bind the method to "this class", otherwise we would be referencing with this the window object!
What's however unclear to me, least from the documentation and so on I viewed, if we use in-built life-cycle methods such as render() or componentDidMount(), majority of the code snippets and also the official documentation seem to not explicitly bind to this
class App extends React.Component {
constructor(){
//....
this.componentDidMount = this.componentDidMount.bind(this)
// is there reason why we don't do this ^ ??
}
}
Is there some in-built binding already inside the React.Component we extend?
Or why don't we need to explicitly bind the life-cycle methods (componentDidMount()) like the rest of our created methods (logIn())?
I've made a component with the following:
...
componentDidMount() {
var that = this;
var x = 0;
}
...
render() {
....
<button onClick={this.componentDidMount}>DID MOUNT</button>
....
}
And the results are in -- when the function does initially mount, that is properly bound, but when clicked from the button, it is not.
Which means that the componentDidMount is not already bound, but it is called from the React internals with the proper context so that it doesn't need to be bound.
-- edit
Perhaps also of note: it's worth checking if you use an autobind package, if that binds the lifecycle methods. autobind-decorator in fact does!
Naturally, this is beceause we need to explicitly bind the method to "this class", otherwise we would be referencing with this the window object!
You can also use arrow functions to been able to use this without binding:
sayHello=()=>{
return 'hello';
}
myOtherFunction=()=>{
console.log('I can acces the other function! Say:'+ this.sayHello())
}
And you don't need to bind the life-cycle methods
Edit: As the documentation says in https://reactjs.org/docs/handling-events.html
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
So it is supposed that lifecycle methods are bound by default.

Class properties for react lifecycle methods

Can I write React lifecycle methods as class properties?
I've been using class properties for a while as I like the fact that I no longer have to manually bind my methods, but I'd like to keep some consistency across my components and I'm wondering if there is any drawback on writing the React lifecycle methods as class properties
import React, { Component } from 'react';
class MyComponent extends Component {
render = () => {
return (
<div>Foo Bar</div>
);
}
}
export default MyComponent;
For example, is the context of this class property affected compared to the context in an equivalent method. Given that the render method in the above code is written as an arrow function, this concern seems relevant.
In a way, the true answer depends on your build pipeline and what the resulting Javascript output looks like. There are two primary possibilities:
Input Code
Let's start by saying you are writing the following before going through any sort of pipeline transformations (babel, typescript, etc):
class Test {
test = () => { console.log('test'); };
}
Output as class member variable.
In one possible world, your pipeline will also be outputting the test function as a member variable for the output class. In this case the output might look something like:
function Test() {
this.test = function() { console.log('test'); };
}
This means that whenever you write new Test() the test function is going to be recreated every single time.
Output as class prototype function
In the other major possibility, your pipeline could be recognizing this as a function property and escape it from the class instance to the prototype. In this case the output might look something like:
function Test() {
}
Test.prototype = {
test: function() { console.log('test'); }
}
This means that no matter how many times you call new Test() there will still be only one creation of the test function around in memory.
Desired behavior
Hopefully it's clear that you want your end result to have the function end up on the prototype object rather than being recreated on each class instance.
However, while you would want the function to not end up as a property, that doesn't necessarily mean you couldn't write it that way in your own code. As long as your build chain is making the correct transformations, you can write it any way you prefer.
Although, looking at the default babel settings (which your babeljs tag leads me to believe you are using) it does not make this transformation for you. You can see this in action here. On the left I've created one class with the function as a property and one class with the function as a class method. On the right hand side, where babel shows it's output, you can see that the class with the function as a property still has it being an instance-level property, meaning it will be recreated each time that class's constructor is called.
I did find this babel plugin, which seems like it might add this transformation in, but I've not used it for myself so I'm not positive.
In my experience, the most reason for writing a method as a class property is when the method will be passed as a callback, and you need it to always be bound to the instance. React lifecycle methods will always be called as a method, so there's no reason to bind them (and you incur a tiny memory penalty when you do). Where this makes a difference is when you're passing a function to a component as a callback (e.g. onClick or onChange).
Take this example:
class BrokenFoo extends React.Component {
handleClick() {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
The function represented by this.handleClick is not automatically bound to the component instance, so when the method tries to read the value of this.props it will throw a TypeError because this is not defined. Read this article if you're not familiar with this; the problem described in section 4.2 "Pitfall: extracting methods improperly" is essentially what's happening when you pass around a method without making sure it's bound correctly.
Here's the class, rewritten with the handler as a class property:
class HappyFoo extends React.Component {
handleClick = () => {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
Effectively, you can think of the handleClick definition in the second example as placing this code into the component's constructor (which is just about exactly the way Babel does it):
this.handleClick = () => {
alert(this.props.message);
}
This achieves the same thing as calling bind on the function (as described in the linked article) but does it a little differently. Because this function is defined in the constructor, the value of this in this.props.message is bound to the containing instance. What this means is that the function is now independent of the calling context; you can pass it around and it won't break.
The rule of thumb that I follow: by default, write methods as methods. This attaches the method to the prototype and will usually behave the way you'd expect. However, if the method is ever written without parentheses (i.e. you're passing the value and not calling it), then you likely want to make it a class property.

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