Fer Toasted
Fer Toasted

Reputation: 1738

Mock axios twice with react-testing-library

I'm testing the component Checkout which has two axios.get calls inside its useEffect, like so:

    useEffect(() => {
        let isMounted = true;

        if (isMounted) {
            axios
                .get("/cart/get-total") // 1st call
                .then((res) => {
                    setCartTotal(res.data)
                    );
                })
                .catch((err) => {
                    console.error(err);

            axios
                .get("/cart/get-subtotal") // 2nd call
                .then((res) => {
                    setUpfrontPayPalPayment(res.data); // 7% comission
                })
                .catch((err) => {
                    console.error(err);
                });
        }

        return () => (isMounted = false);
    }, []);

The first call is mocked using axiosMock from __mocks__/axios.js method:

axios.js

export default {
    post: jest.fn().mockResolvedValue({ data: {} }),
    get: jest.fn().mockResolvedValue({ data: {} }),
    // getAgain: jest.fn().mockResolvedValue({ data: {} }), // nope
};

However, I don't know how to mock the second call which is also a get call.

Checkout.test.js

jest.unmock("axios"); // unmock from previous test
import axiosMock from "../__mocks__/axios";

// faked props
const userInfo = {
    userName: "paco",
    userPhone: "1234567890",
};
const loggedIn = true;

describe("Checkout Component", () => {
    axiosMock.get.mockResolvedValueOnce({ data: 1600 });
    // axiosMock.get.mockResolvedValueOnce({ data: 120 }); // tried this for the second
    // axiosMock.getAgain.mockResolvedValueOnce({ data: 120 }); // also this


    it("renders", async () => {
        await act(async () => {
            render(
                <HashRouter>
                    <Checkout userInfo={userInfo} loggedIn={loggedIn} />
                </HashRouter>
            );
            screen.debug();
        });
    });
});

The Checkout.js component if you want to dive in deeper:

import axios from "axios";
import React, { useEffect, useState, useRef, useContext } from "react";

const Checkout = (props) => {
    // several useStates

    const context = useContext(Context);


    // this method gets fired onSubmit
    const handleTransferSubmit = () => {
        
        // two axios.post consecutive calls that should not affect at
        // all since they are fired onSubmit
    };

    // get user info and cart total-subtotal
    useEffect(() => {
        let isMounted = true;

        // if phone info available set it
        props.userInfo["userPhone"] &&
            setPhone(parseInt(props.userInfo["userPhone"]));

        if (isMounted) {
            axios
                .get("/cart/get-total")
                .then((res) => {
                    setCartTotal(
                        res.data
                    );
                })
                .catch((err) => {
                    console.error(err);
                    context.notifyToaster('error', 'Tenemos problemas con el servidor. Intenta más tarde');
                });

            axios
                .get("/cart/get-subtotal")
                .then((res) => {
                    setUpfrontPayPalPayment(res.data); // 7% comission
                })
                .catch((err) => {
                    console.error(err);
                    context.notifyToaster(
                        "error",
                        "Tenemos problemas con el servidor. Intenta más tarde"
                    );
                });
        }

        return () => (isMounted = false);
    }, []);

    // form validations
    useEffect(() => {
        let isMounted = true;

        return () => (isMounted = false);
    }, [
        orderName,
        phone,
        CP,
        streetName,
        addressNumber,
        deliveryDay,
        deliverySchedule,
        addressDetails,
        paymentMode,
    ]);

    return (
      <div className="container">
        {!loader ? (
          props.loggedIn && cartTotal > 1500 ? (
            <div style={{ marginBottom: "6rem" }}>
              <Form>
                {/* all the form info */}
                <Button
                  className="mb-5"
                  variant="primary"
                  type="submit"
                  disabled={buttonIsActive ? false : true}
                  onClick={handleTransferSubmit}
                >
                  Generar orden
                </Button>
              </Form>
            </div>
          ) : (
            <div>{/* if user is not logged in he can't see the form */}</div>
          )
        ) : (
          <CustomLoader />
        )}
      </div>
    );
};

export default withRouter(Checkout);

Can't get to see the updated component.

If I screen.debug() I notice that cartTotal which is set in the axios mocked response is not yet set when the render happens, causing that the condition for rendering the - Form - component cartTotal > 1500 is false.

Thanks again buddies!

Upvotes: 1

Views: 4451

Answers (1)

Fer Toasted
Fer Toasted

Reputation: 1738

As per @jonrsharpe comments and Jest docs - https://jestjs.io/docs/mock-function-api#mockfnmockresolvedvalueoncevalue - I could mock two consecutive axios calls by chaining mockResolvedValueOnce. I was using them sepparated at the beginning.

Before

axiosMock.get.mockResolvedValueOnce({ data: 1600 });
axiosMock.get.mockResolvedValueOnce({ data: 120});

After

axiosMock.get
    .mockResolvedValueOnce({ data: 1600 })
    .mockResolvedValueOnce({ data: 120 })

Upvotes: 1

Related Questions