Enzyme: Testing component methods

Created on 22 Feb 2016  Â·  58Comments  Â·  Source: enzymejs/enzyme

_I couldn't find any information on this, if this is a duplicate excuse me for stealing your time._

I'm trying to test this application, but I ran into a wall when testing component methods. As an example, take this component:

class Button extends React.Component {
  handleClick() {
    // Do something here
  }
  render() {
    // Component here
  }
}

How could I test the handleClick method here when shallowly rendering this component? (The same question applies to lifecycle methods)

Most helpful comment

@mxstbr const wrapper = shallow(<Button />); wrapper.instance().handleClick()

All 58 comments

@mxstbr const wrapper = shallow(<Button />); wrapper.instance().handleClick()

That is so nice, thanks for the fast response @ljharb!

Dumb question: is there a new way to do this? When I call shallow(<MyComponent/>) the thing that gets returned has no "instance` method; instead it looks like this:

{ '$$typeof': Symbol(react.element),
  type: 'div',
  key: null,
  ref: null,
  props: { className: 'fool', children: [ [Object], [Object] ] },
  _owner: null,
  _store: {} }

@machineghost that's the console inspection which shows enumerable properties. Try actually checking .instance on it.

Thanks for the quick response, but it's not there for me. I get this:

TypeError: choices.instance is not a function

The real code is something like:

let Choices = React.createClass({
    _buildChoices() { ....}
    ....
}
choices = shallowRender(<Choices step={5}/>);
var buttons = choices.instance()._buildChoices();
// get the above error

P.S. I know the message says it's not a function (implying that it might be an object or something), but if I console.log(choices.instance) it's undefined.

Hmm - what version of enzyme and React are you using?

"enzyme": "^2.4.1",
"react": "^0.14.3",

And how do you import "shallowRender"?

Oh dear lord I'm a moron. There was a mistake in my import, and I was actually using a different shallowRender (a home-grown one from pre-Enzyme) without realizing it.

Thank you SO much, and sorry for the stupidity.

Hi

I have tried below solution for calling methods of component and it is working fine for components having state.

But, I have one stateless component and I want to test one method of that component ans I am getting below error.

//Error
` TypeError: wrapper.instance(...).setFocusToTextBox is not a function

  at Object.<anonymous> (src\Engines\LeadershipTrait\__test__\RatingInput.test.js:69:28)

`

// Stateless component method
` const setFocusToTextBox = (index) => {

 window.setTimeout(function () {
        let textbox = document.getElementById("rating"+props.index+index);                    
        textbox.focus();
    },0);
}`

// Testcase
` /*Checking setToFocus method/
it('Checking setToFocus method', () => {

     let enteredRating = [[1,0,0,0,0,0]];        
    //render component for even number row with selectedRating
    let wrapper = shallow(<RatingInput index={oddIndex} ratingEntered={enteredRating} rating={rating}/>);
    wrapper.instance().setFocusToTextBox(1);        
});`

Please guide where i am doing mistake.

Thank you,
Nikunj.

Stateless components can't possibly have any methods, because they're not instances. That's just a function closed inside the SFC, and it's impossible to invoke it from tests unless you extract it from the rendered jsx.

Thanks for guidance.

I'm having trouble when I try to get the wrapper instance for a component which is written using class.

class MyComponent extends Component {
  myMethod() {
    // ...
  }
}

// on test files
const wrapper = shallow( <MyComponent /> ).instance();

wrapper.myMethod() // -> undefined

In short, the wrapper instance doesn't have the methods of the Component.
Am I doing something wrong?

@retrofox that seems to work - if it didn't have the method, it would throw a TypeError. Returning undefined might be what myMethod does?

@retrofox that seems to work - if it didn't have the method, it would throw a TypeError. Returning undefined might be what myMethod does?

Nop. The method is not defined.

TypeError: wrapper.myMethod is not a function
      at Context.<anonymous> (index.jsx:81:8)

@retrofox what does instance() return?

sorry @aweary. I was wrapping the MyCompnent component with a translation helper. I don't know exactly what is the issue in our code but definitely I'll have to dive on this.
Thanks for your comments and I'm sorry for disturbing.

@ljharb I saw this in one of your comments above.
const wrapper = shallow(

@smoholkar i'll have to see the implementation of Num to answer that.

@ljharb It's huge but this is the skeleton of it :
``

class Num extends React.Component {

state = {
  value = this.props.path || ''
}

componentWillReceiveProps(newProps) {
    if (newProps.path !== undefined && this.props.path !== newProps.path) {
      this.setState({ value: newProps.path });
    }
}

getNumberDetails() {
// ......
// does some processing & then returns the value
returns value 
}

render() {
  const result = getNumberDetails() // I'm testing for correctness of the value of result. 

return (
   <Wrapper1 value = {result}>
        <Wrapper2 value = {result}> 
        </Wrapper2>
   <Wrapper1>
)

}


It seems like you want to setProps and update your wrapper, and then before and after, assert on .prop('value')

Hey guys,

I have the problem that i mounted a redux connected component. When i call .instance() on it i do not have access to the class functions.
I also cannot shallow render it because the component has a lot of initialization code in the constructor.

@HelgeWieding use .dive() to dive through the wrapper.

Hi guys, I use mocha-enzyme-chai-sinon to testing the react router v4. It's really challenging for me to write a unit test for router v4. So I need to test is redirect really. You can check my routes.js http://stackoverflow.com/questions/43604837/react-router-v4-unit-test

`

    describe('<Routes/>', () => {

      let routes
      let redirectToLoginPage
      let login

     beforeEach(() => routes = shallow(<Routes redirectToLoginPage={redirectToLoginPage = sinon.spy()} login={login = sinon.spy()}/>))

     it.only('should login on entering /login-callback or redirect to / ', () => {
      let result = routes.find(Route).findWhere(x => x.props().path === '/login-callback').props().render()

       login.calledOnce.should.be.true
       result.find(Redirect).length.should.be.equal(1)
       result.props.to.should.be.equal('/')

})`

But I catch the error in the console the find is not a function. When I console result, I see what that expect. how to test the [Function: Redirect] - really redirect

{ '$$typeof': Symbol(react.element),
  type:
   { [Function: Redirect]
     propTypes: { push: [Object], from: [Object], to: [Object] },
     defaultProps: { push: false },
     contextTypes: { router: [Function: bound checkType] } },
  key: null,
  ref: null,
  props: { to: '/', push: false },
  _owner: null,
  _store: {} }

I solve the main problem, but why I receive the
expected to be of type [Function: Redirect], but it is of type null
expect(result).to.have.type(Redirect)

@palaniichukdmytro your comments here seem duplicates of your comments on #736.

@ljharb using your technique is causing this error:

ShallowWrapper::instance() can only be called on the root

My package json:

"jest": "^20.0.4",
"jest-enzyme": "^3.0.1",
"enzyme": "^2.8.2",
"react-redux": "^5.0.4",
"redux": "^3.6.0",

My component is connected to redux so I have to render it with a provider store like so:

import MockProvider from "<path>/MockProvider";
...
const wrapper = shallow(
  <MockProvider>
    <ConnectedComponentToTest />
  </MockProvider>
);
wrapper.find(ConnectedComponentToTest).instance().methodToTestOnClass();

I hope others are having this issue, I'm not sure how to combat this. Thanks for taking a peek.

Use .dive, not .find. If you use find, you have to call .shallow() on it first.

In case anyone else has this issue, I simply had to dive in order to get it to work.

test('StatefulContainer has custom business logic', () => {
  const wrapper = shallow(
    <StatefulContainer store={store} />
  );
  const result = wrapper.dive().instance()._privateFunc()
  expect(result).toEqual(2);
});

That's a typescript flaw; not an enzyme one.

@FDiskas

const instance = wrapper.instance() as MyComponent
instance.handleClick()

@adc17 i'm pretty sure https://github.com/airbnb/enzyme/issues/208#issuecomment-269795536 is still accurate; .props gets you all your props. SFCs don't have "functions", because functions don't have functions.

Curious, is there a way to unit test a component's method without invoking the lifecycle methods with shallow?

I want to specifically test a component method that calls this.setState, and so I can't move the method out to an utils file. I also don't want to invoke the lifecycle methods since they are dependent on a lot of props being passed in, and thus I would have to do a lot of mocking.

@rencire shallow(element, { disableLifecycleMethods: true }) (also, the option can be passed to the entire adapter configure call)

@ljharb thanks, it seems componentWillMount is still getting called. Docs imply that only componentDidMount and componentDidUpdate are disabled?

Also running enzyme 2.9.1.

ah, yes, that is true. However, componentWillMount executes as part of the first render, so any props it needs should be passed in regardless.

(You should also update to enzyme 3 :-) )

Is there any good tutorial on how to use all this dive(), find() and instance() stuff? I am scratching my head for hours now to get this work in a non-trivial scenario, where components are wrapped in Redux stores and IntlProviders but I have to call one of their methods. This API is really pretty complex.

Worked perfectly. Thanks for this.

how to mock a method of component which uses store.
wrapper.instance.method() is not working for my componnt as it is using Store

@ashraf1994 export your component without a connection
export { App as AppDisconnected };
You can check example
https://github.com/nfq-eta/react-typescript/blob/a7e6eb2e4e5191833454a9e29d314d64107e97ad/src/client/containers/AppContainer.tsx#L90

I don't recommend doing that; if it's never used in production without the connection, it should never be tested without the connection.

All data in tests should be mocked including store. So this is more easy todo so. Export disconnected component and pass all required props that comes from store and test them.

It's easier, but it's still wrong. Prefer correctness over convenience.

You mean I should write tests like

const something = () => 2*2
expect(2*2).toEqual(something());

It's wrong. You should not add result as expectancy criteria. Do it like

const something = () => 2*2
expect(something()).toEqual(4);

You are probably talking about an integration tests is used to check that different pieces of the system work together. Integration tests cover whole applications.

But this is not the right place to discuss.

Peace :mega:

@ljharb let's assume for a minute that this is the given state of the component.

class Button extends React.Component {
  handleClick = () => {
    // make props.answer = 2 through magic
  }
  render = () => (
      <div onClick={this.handleClick}/>
  )
}

Wouldn't it be better to test by simulating the click if and only if you know it's hooking to the click event?

it('should do something after user clicks the button', () => {
  const wrapper = render(<Button/>);

  wrapper.simulate('click');

  expect(props.answer).toEqual(2);
});

The example you provided above makes a lot of sense since you do not know the behaviour to test yet so you have to go back to your implementation details and test it.

By going above one more level and simulating the click instead of invoking the method in that specific instance, you can safely refactor handleClick and still be able to test the behaviour that you want.

class Button extends React.Component {
-  handleClick = () => {
+  somethingElse = () => {
    // make props.answer = 2;
  }
  render = () => (
-      <div onClick={this.handleClick}/>
+      <div onClick={this.somethingElse}/>
  )
}

If a refactor (no behaviour changes) happens then my test is still fine. Makes sense because I am testing the behaviour of the component (not the method as OP mentioned).

There's nothing wrong with your example btw. Yours will be invoking the implementation since you do not know which one handleClick will be hooking on i.e. I can very well do something like onChange={handleClick}. I'm simply showing a different way to test when you get more details.

@retrofox i don't know if you resolved the issue - a year later lol but i had to dive() into my wrapper before executing the instance, after that my class methods were available

What if I have HOC'ed React component MyComponentClass for instance? How to type wrapper instance of enzyme?

// MyComponent.tsx
// ...
export const MyComponent = withDefaultProps<MyComponentProps>(defaultProps)(MyComponentClass);
// ...

// MyComponent.spec.tsx
// ...
const wrapper = shallow(<MyComponent />);
const instance = wrapper.instance() as MyComponent;
instance.classMethod(); // [ts] Property 'classMethod' does not exist on type 'Component<{}, {}, any>'.
// ...

Anyone has solved this already?

@karolisgrinkevicius enzyme isn't written in TypeScript, so this repo really isn't the place to ask that - i'd suggest some typescript-related chatrooms.

@ljharb people are lending here from google, why not let them find the right answer here?

@FDiskas if they read my comment, and end up in actual places that provide typescript help, they'll be much better off than if they get an answer to one off-topic question here.

How to test below method

 function getDateTimeFormatted(value){
    if (typeof value == "undefined") { return "" ; }
   return moment.utc(value.valueOf()).locale("en").format("MM/DD/YYYY hh:mm:ss A");
}

@subrat7 this article would clarify out things about mocking date and momentjs in particular.

In case anyone else has this issue, I simply had to dive in order to get it to work.

test('StatefulContainer has custom business logic', () => {
  const wrapper = shallow(
    <StatefulContainer store={store} />
  );
  const result = wrapper.dive().instance()._privateFunc()
  expect(result).toEqual(2);
});

But where is your definition of store? Would love to know how that looks...

@mxstbr const wrapper = shallow(<Button />); wrapper.instance().handleClick()

what is instance()

@42b883 an enzyme method that exposes the underlying class component instance.

Thx

On Mon, May 27, 2019, 21:01 Jordan Harband notifications@github.com wrote:

@42b883 https://github.com/42b883 an enzyme method that exposes the
underlying class component instance.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/airbnb/enzyme/issues/208?email_source=notifications&email_token=AKPNB3LRDZCD3EAW6UZHSSLPXPZTRA5CNFSM4B37MCTKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWKALWY#issuecomment-496240091,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AKPNB3IIEITLH43IM2WV7ELPXPZTRANCNFSM4B37MCTA
.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

heikkimu picture heikkimu  Â·  3Comments

andrewhl picture andrewhl  Â·  3Comments

AdamYahid picture AdamYahid  Â·  3Comments

potapovDim picture potapovDim  Â·  3Comments

modemuser picture modemuser  Â·  3Comments