如何测试依赖setState的已卸载React组件的方法?

时间:2019-02-10 00:57:19

标签: javascript reactjs testing jestjs

我已经实现了一个组件(用于打字培训应用程序),该组件可以在全球范围内跟踪按键,如下所示:

class TrainerApp extends React.Component {
    constructor() {
        // ...
        this.handlePress = this.handlePress.bind(this);
    }
    handlePress(event) {
        const pressedKey = event.key;
        const task = this.state.task;
        const expectedKey = task.line[task.position];

        const pressedCorrectly = pressedKey == expectedKey;
        this.setState(prevState => {
            const newPosition = prevState.task.position +
                (pressedCorrectly ? 1 : 0);
            return {
                // ...prevState, not needed: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-are-merged
                task: {
                    ...prevState.task,
                    position: newPosition,
                    mistakeAtCurrentPosition: !pressedCorrectly
                }
            }
        })
    }
    componentDidMount() {
        document.addEventListener(this.keyEventTypeToHandle,this.handlePress);
    }
    componentWillUnmount () {
        document.removeEventListener(this.keyEventTypeToHandle,this.handlePress);
    }
    ...
}

,我想用Jest编写一些单元测试。我最初的想法是:

describe('TrainerApp.handlePress should',() => {
    test('move to the next char on correct press',() => {

        const app = new TrainerApp();
        app.state.task.line = 'abc';
        app.state.task.position = 0;
        const fakeEvent = { key: 'a' };

        app.handlePress(fakeEvent);

        expect(app.state.task.position).toBe(1);
    });
    ...
});

但问题是app.handlePress依赖于this.setState的用法,而尚未安装组件时,该用法尚未定义。当然,我可以这样修改app

test('move to the next char on correct press',() => {

    const app = new TrainerApp();
    app.setState = jest.fn(function(handler) {
        this.state = handler(this.state);
    });
    app.state.task.line = 'abc';
    app.state.task.position = 0;
    const fakeEvent = { key: 'a' };

    app.handlePress(fakeEvent);

    expect(app.state.task.position).toBe(1);
});

甚至是这样:

class ExplorableTrainerApp extends TrainerApp {
    setState(handler) {
        this.state = handler(this.state);
    }
}
test('move to the next char on correct press',() => {

    const app = new ExplorableTrainerApp();
    app.state.task.line = 'abc';
    app.state.task.position = 0;
    const fakeEvent = { key: 'a' };

    app.handlePress(fakeEvent);

    expect(app.state.task.position).toBe(1);
});

但是这似乎是一种非常脆弱的方法(在此我依靠这样的事实:.setState是通过函数参数调用的,而仅可以使用newState参数来调用,因此我正在测试实现细节,而不只是行为。是否有更健壮的方法可以测试这一点?

1 个答案:

答案 0 :(得分:1)

有一些用于测试React组件的框架,Enzymereact-testing-library都很流行并且得到了很好的支持。