我想测试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
?稍后,我希望测试状态和本地存储的副作用,但我不理解此错误。
答案 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
});
});