简体   繁体   中英

How to test a component function being called when componentDidMount?

I have a React JS component MyComponent and I would like to test the following use case:

It should call updateSomething() when component on mount

And I've come up with the following code:

System Under Test (SUT)

export class MyComponent extends React.Component<Props, State> {
    public componentDidMount() {
        console.log("componentDidMount"); // Debug purpose.
        this.fetchSomething()
            .then(() => {
                console.log("fetchSomething"); // Debug purpose.
                this.updateSomething();
            });
    }

    // These are public for simplicity
    public async fetchSomething(): Promise<any> { }
    public updateSomething() {
         console.log("updateSomething"); // Debug purpose.
    }
}

Test

it("should update something, when on mount", () => {
    const props = { ...baseProps };
    sinon.stub(MyComponent.prototype, "fetchSomething").resolves();
    const spy = sinon.spy(MyComponent.prototype, "updateSomething");

    shallow(<MyComponent {...props} />);

    sinon.assert.calledOnce(spy);
});

The result is the test failed with AssertError: expected updateSomething to be called once but was called 0 times but all three console.log() printed.

My understanding is since I want to test the event when on mount, I have to spy/stub it before it's even created, therefore I have to spy on MyComponent.Prototype . Also, for fetchSomething() , I have to stub the async call and make it .resolves() to let it progress.

But I couldn't understand how it can still console.log("updateSomething") without being spied.

I don't know about sinon and I don't know about ts , but with simple js and jest it'd be like this:

fetchSomething() = Promise.resolve();

Then, in your test, you wouldn't have to mock it and just use:

const spy = jest.spyOn(MyComponent.prototype, 'updateSomething');

To see if it was called:

expect(spy).toHaveBeenCalled();

According to the comments/answer from this post , the assertion comes before .updateSomething have been called. To solve this problem, I would've to await the componentDidMount lifecycle method.

So the fixed program is as below:

// SUT

public async componentDidMount() {
    //...
    return this.fetchSomething()
        .then(() => {
            //...
        });
}

// Test

it("should update something, when on mount", () => {
    const props = { ...baseProps };

    // Disable lifecycle here to enable stub in between.
    const wrapper = shallow(<MyComponent {...props} />, { disableLifecycleMethods: true });

    sinon.stub(wrapper.instance(), "fetchSomething").resolves();
    const stub = sinon.stub(wrapper.instance(), "updateSomething");

    // Actually call component did mount.
    wrapper.instance().componentDidMount().then(() => {
         sinon.assert.calledOnce(stub);
    });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM