【问题标题】:How do I test the method shouldComponentUpdate?如何测试方法 shouldComponentUpdate?
【发布时间】:2019-12-29 17:13:12
【问题描述】:

我想测试该方法,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?后来,我希望测试状态和本地存储的副作用,但我不明白这个错误。

【问题讨论】:

  • 需要进行状态更改才能触发shouldComponentUpdate。您还应该测试函数的结果,而不是如果它被调用。
  • 我认为文档说,“使用 shouldComponentUpdate() 让 React 知道组件的输出是否不受当前状态或道具变化的影响。默认行为是在每个状态上重新渲染更改,并且在绝大多数情况下,您应该依赖默认行为”。 https://reactjs.org/docs/react-component.html#shouldcomponentupdate 所以我认为如果在 shouldComponentUpdate 中指定,道具的变化也应该触发 true?
  • 您还提到要测试该功能的副作用。我也尝试这样做。 ```expect(wrapper.find("MyComponent").state("newStorage")).to.be.true; ``` 导致这个错误:``` AssertionError: expected false to be true ```

标签: javascript reactjs testing enzyme sinon


【解决方案1】:

您正在处理的 shouldComponentUpdate 生命周期是错误的。你不在其中setState,而是只返回一个boolean(true 或false)让React 知道何时重新渲染DOM。这允许您阻止来自外部道具的更新。例如:

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

上述状态:如果当前 newStorage 状态与下一个 newStorage 状态不匹配,则重新渲染组件。 以上内容会阻止 prop 更改重新渲染组件。如果您更改了 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 });
   }
}

上面检查当前的 props 是否与之前的 props 不匹配,然后更新状态以反映变化。

您将无法使用static getDerivedStateFromProps,因为没有新旧道具之间的比较,而只是与传入道具和当前状态的比较。也就是说,如果您将某些内容存储在与存储道具直接相关的状态中,则可以使用它。一个示例可能是,如果您将密钥从道具存储到状态中。如果 props 键要更改并且它与状态中的当前键不匹配,那么它将更新状态以反映键更改。在这种情况下,您将返回 object 以更新状态或返回 null 以不更新状态。

例如:

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

在测试方面,您需要手动更新 props 以影响状态更改。通过针对状态更改进行测试,您将间接针对 componentDidUpdate 进行测试。

例如(你必须mock localStorage,否则状态不会更新——我还建议exporting 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 
  });
});

【讨论】:

    猜你喜欢
    • 2017-10-27
    • 2019-06-04
    • 2016-11-02
    • 2017-05-06
    • 2016-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多