Home > Blockchain >  Cannot spyOn on a primitive value; undefined given - Testing a function execution in onChange in con
Cannot spyOn on a primitive value; undefined given - Testing a function execution in onChange in con

Time:01-21

New to testing and This is a connected component using redux. I am testing if the maskPromoCode function is called. With the current test code, the error says: Cannot spyOn on a primitive value; undefined given

Do not understand why. Thanks.

The component:

class Textbox extends React.Component {
  constructor(props) {
      super(props);
  }

 onChange(e) {
    e.preventDefault();
    this.setState({
      value: this.maskPromoCode(e.target.value) 
    })
 }

  maskPromoCode(value) {...}

  render() {
    return (
      <>
        <input
          ...
          onChange={e => this.onChange(e)} 
        />  
      </>
    );
  }
}
const mapDispatchToProps = { setError, setPwError };
export default connect(null, mapDispatchToProps)(Textbox);

The test file

it('invokes the maskPromoCode function with event value', () => {
    const funcMock = jest.spyOn(Textbox.prototype, 'maskPromoCode');
    
    const wrapper = mount(
      <Provider store={store}>
        <Textbox {...baseProps} name="redeem-promo" />
      </Provider>
    );

    const inputField = wrapper.find('input');
    const event = {
      target: {
        value: 'event value',
      },
    };
    inputField.simulate('change', event);
    expect(funcMock).toHaveBeenCalledWith(event.target.value);
  });

CodePudding user response:

  1. The Textbox component is wrapped by the connect function, it becomes a HOC that has no prototype, I guess you import the HOC in your test file. The TextBox component imported in the test file is a HOC. So TextBox.prototype is undefined, that's why jest.spyOn throws error.

  2. If you insist to use jest.spyOn() for the maskPromoCode method, you can use jest.spyOn(Textbox.WrappedComponent.prototype, 'maskPromoCode'). There is a WrappedComponent static property on HOC, so you can get the original component class.

  3. Recommend: You are testing the implementation details if using jest.spyOn(...), you should test the component behavior.

E.g.

Textbox.jsx:

import React from 'react';
import { connect } from 'react-redux';

class Textbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: '131' };
  }

  onChange(e) {
    e.preventDefault();
    this.setState({ value: this.maskPromoCode(e.target.value) });
  }

  maskPromoCode(value) {
    return '***';
  }

  render() {
    return (
      <>
        <input value={this.state.value} onChange={(e) => this.onChange(e)} />
      </>
    );
  }
}
export default connect(null, null)(Textbox);

Textbox.test.jsx:

import Textbox from './Textbox';
import React from 'react';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';

describe('70795348', () => {
  it('invokes the maskPromoCode function with event value', () => {
    console.log(Textbox.WrappedComponent.prototype.maskPromoCode);
    const mockStore = configureStore();
    const store = mockStore();
    const wrapper = mount(
      <Provider store={store}>
        <Textbox name="redeem-promo" />
      </Provider>
    );
    expect(wrapper.find('input').instance().value).toBe('131');
    const event = { target: { value: 'event value' } };
    wrapper.find('input').simulate('change', event);
    expect(wrapper.find('input').instance().value).toBe('***');
  });
});

Test result:

 PASS  stackoverflow/70795348/Textbox.test.jsx
  70795348
    ✓ invokes the maskPromoCode function with event value (40 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.729 s, estimated 9 s
  •  Tags:  
  • Related