我如何测试方法shouldComponentUpdate?

时间:2019-08-24 23:29:34

标签: javascript reactjs testing enzyme sinon

我想测试shouldComponentUpdate方法,但是我写的测试没有副作用。我该如何测试?

我已经尝试过这里提出的解决方案,但是有一些问题,我将在代码中显示:How to unit test React Component shouldComponentUpdate method

我尝试使用shallow而不是mount,但是当我这样做时,我的测试此时失败了:

    expect(shouldComponentUpdate).to.have.property("callCount", 1);
    // AssertionError: expected [Function: proxy] to have a property 'callCount' of 1, but got 0

这是我组件的shouldComponentUpdate函数:

    shouldComponentUpdate(nextProps, nextState) {
        if (this.props.storageValue !== nextProps.storageValue) {
            localStorage.setItem(constantFile.storageKey, nextProps.storageValue);
            localStorage.setItem(constantFile.timestampKey, now());
            this.setState({
                newStorage: nextProps.storageValue
            });
            return true;
        }

        ...

        return false;
    }

这是我的考试:

    it("Test shouldComponentUpdate", () => {
        /* eslint-disable one-var */
        const wrapper = mount(
            <Provider store={store}>
                <MyComponent />
            </Provider>),
            shouldComponentUpdate = sinon.spy(CapitalHomeAccessPointContainer.prototype, "shouldComponentUpdate");
        wrapper.setProps({
            storageValue: true
        });
        expect(shouldComponentUpdate).to.have.property("callCount", 1);   
        expect(shouldComponentUpdate.returned(true)).to.be.equal(true);
  expect(localStorage.getItem(constantFile.timestampKey)).to.equal(someExpectedTimestamp);
    });
expect(shouldComponentUpdate).to.have.property("callCount", 1);

失败

AssertionError: expected false to equal true

为什么会这样?我认为shouldComponentUpdate应该返回了true?稍后,我希望测试状态和本地存储的副作用,但我不理解此错误。

1 个答案:

答案 0 :(得分:2)

您正在处理shouldComponentUpdate生命周期错误。您没有在其中setState,而是只返回一个boolean(对或错),以使React知道何时重新渲染DOM。这使您可以阻止来自外部道具的更新。例如:

shouldComponentUpdate(nextProps, nextState) {
 return this.state.newStorage !== nextState.newStorage
}

以上状态:如果当前newStorage状态与下一个newStorage状态不匹配,则重新呈现该组件。 以上内容将阻止更改道具重新呈现组件。如果您更改了storage道具,它将不会更新该组件,因为状态没有更改。您要使用shouldComponentUpdate来防止不必要的双重重新渲染和/或防止父组件的重新渲染不必要地重新渲染子组件。在这种情况下,您将不需要使用此生命周期方法。

相反,您应该使用static getDerivedStateFromProps来使道具在渲染之前更新状态,或者使用componentDidUpdate来在渲染之后更新状态。

在您的情况下,由于您只想在this.props.storageValue更改时更新状态,因此您应该使用componentDidUpdate

componentDidUpdate(prevProps) {
   if (this.props.storageValue !== prevProps.storageValue) {
     localStorage.setItem(constantFile.storageKey, nextProps.storageValue);
     localStorage.setItem(constantFile.timestampKey, now());
     this.setState({ newStorage: nextProps.storageValue });
   }
}

上面的命令检查当前道具是否与以前的道具不匹配,然后更新状态以反映更改。

您将无法使用static getDerivedStateFromProps,因为新旧道具之间没有比较,只有与传入的道具和当前状态进行比较。也就是说,如果您存储的状态与存储道具直接相关,则可以使用它。例如,如果您存储了道具中的钥匙并进入状态。如果props键要更改,并且与状态中的当前键不匹配,则它将更新状态以反映键的更改。在这种情况下,您将返回object来更新状态,或者返回null来不更新状态。

例如:

static getDerivedStateFromProps(props, state) {
  return props.storage.key !== state.storageKey 
  ? { storageKey: props.storage.key }
  : null
}

在测试时,您需要手动更新道具以影响状态更改。通过测试状态更改,您将间接测试componentDidUpdate

例如(您必须mock localStorage,否则状态将不会更新-我也建议export放入class并导入它,而不要使用Redux连接的组件):

import { MyComponent } from "../MyComponent.js";

const initialProps = { storageValue: "" };

describe("Test Component", () => {
  let wrapper;
  beforeEach(() => {
    wrapper = mount(<MyComponent { ...initialProps } />);
  })

  it("updates 'newStorage' state when 'storageValue' props have changed, () => {
    wrapper.setProps({ storageValue: "test" });  
    expect(wrapper.state('NewStorage')).toEqual(...); // this should update NewStorage state

    wrapper.setProps({ someOtherProp: "hello" )};
    expect(wrapper.state('NewStorage')).toEqual(...); // this should not update NewStorage state 
  });
});
相关问题