如何在 React 组件上测试 prop 更新

IT技术 unit-testing jasmine reactjs
2021-03-26 18:44:15

单元测试 React 组件 prop 更新的正确方法是什么。

这是我的测试夹具;

describe('updating the value', function(){
        var component;
        beforeEach(function(){
            component = TestUtils.renderIntoDocument(<MyComponent value={true} />);
        });

        it('should update the state of the component when the value prop is changed', function(){
            // Act
            component.props.value = false;
            component.forceUpdate();
            // Assert
            expect(component.state.value).toBe(false);
        });
});

这工作正常并且测试通过,但是这会显示一条react警告消息

'Warning: Dont set .props.value of the React component <exports />. Instead specify the correct value when initially creating the element or use React.cloneElement to make a new element with updated props.'

我只想测试一个属性的更新,而不是创建具有不同属性的元素的新实例。有没有更好的方法来进行此属性更新?

6个回答

AirBnB 的库为这个问题提供了一个优雅的解决方案。

它提供了一个 setProps 方法,可以在浅层或 jsdom 包装器上调用该方法。

    it("Component should call componentWillReceiveProps on update", () => {
        const spy = sinon.spy(Component.prototype, "componentWillReceiveProps");
        const wrapper = shallow(<Component {...props} />);

        expect(spy.calledOnce).to.equal(false);
        wrapper.setProps({ prop: 2 });
        expect(spy.calledOnce).to.equal(true);
    });
但是 OP 没有提到酶,也没有在他们的标签中提到
2021-05-28 18:44:15
我不建议在 '21 中使用这种方法进行测试,尽管我不知道您正在测试什么,但我将专注于断言 prop 更改的应用程序在组件输出中正确呈现,而不是 prop 本身的值.
2021-05-28 18:44:15
如果我们已经安装了组件怎么办?
2021-06-05 18:44:15

如果在同一个容器节点中使用不同的 props 重新渲染元素,它将被更新而不是重新安装。请参阅React.render

在您的情况下,您应该ReactDOM.render直接使用而不是TestUtils.renderIntoDocument. 后者每次被调用时都会创建一个新的容器节点,因此也是一个新组件。

var node, component;
beforeEach(function(){
    node = document.createElement('div');
    component = ReactDOM.render(<MyComponent value={true} />, node);
});

it('should update the state of the component when the value prop is changed', function(){
    // `component` will be updated instead of remounted
    ReactDOM.render(<MyComponent value={false} />, node);
    // Assert that `component` has updated its state in response to a prop change
    expect(component.state.value).toBe(false);
});
如果您正在测试对诸如componentWillReceiveProps或 之类的道具更改的响应,componentDidUpdate我也会这样做。也就是说,如果可能的话,我会尝试重写组件,以便状态中的值在渲染时动态计算(而不是使用状态)。
2021-05-24 18:44:15
+1 为答案。请注意,这现在ReactDOM.render不仅仅是React.render(我认为从 0.14 版开始)。
2021-06-01 18:44:15
双方TestUtils.renderIntoDocumentReactDOM.render使用从返回的值ReactDOM.render根据React 文档:“使用这个返回值是遗留的,应该避免,因为在某些情况下,React 的未来版本可能会异步渲染组件”。有没有更好的解决方案可以避免使用 ReactDOM.render 输出?
2021-06-06 18:44:15
如果MyComponent 包裹在 a 周围Provider呢?
2021-06-13 18:44:15
在实际实践中,并非在所有情况下都显式调用渲染。更好的方法是使用状态数据更改来触发重新渲染,因为在实时环境中可能还有一些其他钩子,您通过直接调用状态更改而错过了这些钩子。
2021-06-17 18:44:15

警告:这实际上不会改变props。

但对我来说,我想要的只是在componentWillReceiveProps. 所以我myComponent.componentWillReceiveProps(/*new props*/)直接打电话

我不需要/想要测试 React 在 props 更改时调用方法,或者 React 在 props 更改时设置 props,只是如果 props 与传入的不同,则会触发一些动画。

快速添加,因为我正在寻找testing-library的答案,但在这里没有找到:这个问题中有一个例子,它看起来像这样:

const {container} = render(<Foo bar={true} />)

// update the props, re-render to the same container
render(<Foo bar={false} />, {container})

或者,testing-library 现在提供了rerender一种完成同样事情的方法。

这是一个较旧的问题,但如果其他人偶然发现此问题,以下设置对我来说很好用:

it('updates component on property update', () => {
    let TestParent = React.createClass({
        getInitialState() {
            return {value: true};
        },
        render() {
            return <MyComponent value={this.state.value}/>;
        }
    });
    component = TestUtils.renderIntoDocument(<TestParent/>);
    component.setState({value: false});
    // Verification code follows
});

这使得 React 运行通常的组件更新。