简体   繁体   中英

How do I mock "this" when writing a unit test for a React module?

I'm writing a React app where the App.js has a lot of logic inside it tying together the rest of the components and keeping a coherent state. I have moved some of the code that were originally in App.js to helpermodule.js and bind the imported functions so that they can manipulate the state of the App component.

This is my setup:

App.js

import helperFunction from './helpermodule'

class App extends Component {
  constructor(props) {
    super(props)

    this.state = { foo: 'bar' }
    this.helperFunction = helperFunction.bind(this)
  }
}

helpermodule.js

function helperFunction() {
  this.setState({
    bar: 'calculated' + this.state.foo,
  })
}

export { helperFunction }

And I want to write a unit test for the function(s) inside the helpermodule. I can't find the relevant parts in the Enzyme or Jest documentation where this is covered. The goal is to see what effect helperFunction has on the App state.

I have tried things like this

test('helperFunction', () => {
  let state = { foo: 'bar' }
  let setState = (obj) => { this.state = obj }

  return expect(helperFunction().bind(this))
    .toBe({
      ...newShift,
      id: 0,
    })
}

But that just returns an error; TypeError: Cannot read property 'state' of undefined . Perhaps my entire approach is wrong so the problem can be circumvented but I'm not sure.

Don't use this.setState in an external function

helpermodule.js

function helperFunction(stateFoo) {
  return {bar: 'calculated' + stateFoo};
}

export { helperFunction }

Then use the result to set state in the component. Also don't setState in constructor.

import helperFunction from './helpermodule'

class App extends Component {
  constructor(props) {
    super(props)

    this.state = { foo: 'bar', ...helperFunction('bar') }
  }
}

This will make testing easier as well as being a better structure in general.

no idea why would you follow this approach it is so weird and hard to follow and maintain but for the sake of your argument. the issue is that you are passing this to the bind while this has no property of state or setState so you should pass a mocked this instead.

here is how it could go

describe('helperFunction', () => {
    function helperFunction() {
        // @ts-ignore
        this.setState({
            // @ts-ignore
            bar: 'calculated' + this.state.foo,
        })
    }

    it('updates the provided stateHolder', () => {
        const stateHolder = {
            state: { foo: 'bar' },
            // don't use arrow function otherwise it won't react the state via this
            setState(obj: any) {
                this.state = obj;
            },
        };

        const importedHelperFunction = helperFunction.bind(stateHolder);
        importedHelperFunction();

        expect(stateHolder.state.foo).toBe('calculatedbar');
    });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM