Home > Enterprise >  Solved - Mocking axios with custom error in Jest and Typescript
Solved - Mocking axios with custom error in Jest and Typescript

Time:07-22

I want to mocking axios post request with custom error object.

getToken.ts

const GetToken = async (params:ITokenParams):Promise<ITokenResult | IError>=>{
    try{
        const api = axios.create({
            headers: {
              "Access-Control-Allow-Origin": "*",
              "Content-Type": "application/json",
            },
        });

        const {data} = await api.post<ITokenResult>("https://someURL", { ...params });
        return data;
    }
    catch(e){
        let err:IError={
            error:true,
            module:path.basename(__filename),
            method:GetToken.name
        };
        if(e instanceof AxiosError && e.response?.data?.errors){
            let errorsArray:IApiError[]=e.response?.data?.errors;
            err.message=errorsArray.length>0?errorsArray[0].detail : e.message;
        }
        else if (e instanceof Error){
            err.message=e.message
        }
        return err;
    }
}

export default GetToken;

The custom error object is mockResponse. I want to return a response with status 401. In response, data object should includes errors array.

getToken.test.ts

import {axiosInstance} from '../../utils/axios-instance';
import GetToken from '@App/api/oauth/getToken';

jest.mock('../../utils/axios-instance.ts');

describe('Token from OAuth', () => {
    test('Return error for expired code', async () => {
        const mockResponse = {
           data:{}, 
           status: 401,
           statusText:"Unauthorized",
           headers: {}, 
           config: {} , 
           response:{ 
              data: { errors: [{ detail: 'a' }] } 
           }
        };

        const response = {
            error: true,
            module: 'getToken.ts',
            method: 'GetToken',
            message: 'a'
        };

        const mockedAxiosInstance = axiosInstance as jest.MockedFunction<typeof axiosInstance>;
        (mockedAxiosInstance.post as jest.Mock).mockRejectedValueOnce(mockResponse);

        const result= await GetToken({
            client_id:process.env.APPLICATION_ID,
            client_secret:process.env.APPLICATION_SECRET,
            code:"some-codes",
            grant_type:"authorization_code"
        });

        expect(result).toEqual(response);
    })
})

axios-instance.ts

import axios from "axios";
export const axiosInstance = axios.create({
    headers: {
        "Access-Control-Allow-Origin": "*",
        "Content-Type": "application/json",
    }
})

The test is fail.

  Object {
    "error": true,
-   "message": "a",
    "message": "Request failed with status code 400",
    "method": "GetToken",
    "module": "getToken.ts",
  }

When i check the code in the catch statement this block runs

  else if (e instanceof Error){
        err.message=e.message
  }

How can i return my custom error object?

CodePudding user response:

Yes I solve with 2 different option.

Thanks to @jonrsharpe, axios instance should be same both .test.ts and .ts files.

apiRoot.ts

import axios from "axios";
export const apiRoot = axios.create({
    headers: {
        "Access-Control-Allow-Origin": "*",
        "Content-Type": "application/json",
    }
})

getToken.ts

...
import {apiRoot} from '@App/utils/apiRoot';

const GetToken = async (params:ITokenParams):Promise<ITokenResult | IError>=>{
    try{
        // This line change with apiRoot. We don't use axios.create.
        const {data} = await apiRoot.post<ITokenResult>("someURL", { ...params });
        return data;
    }
    catch(e){
      ...
    }
}

export default GetToken;

Option-1 : Test with axios

getToken.test.ts

import {apiRoot} from '@App/utils/apiRoot';
import GetToken from '@App/square-api/oauth/getToken';
import {AxiosError} from 'axios';

//mock the instance from apiRoot
jest.mock('@App/utils/apiRoot.ts');

describe('Token from OAuth', () => {
    test('Return error for expired code', async () => {
        // Expected response from GetToken method
        const response = {
            error: true,
            module: 'getToken.ts',
            method: 'GetToken',
            message: 'a'
        };

        const mockedAxiosInstance = apiRoot as jest.MockedFunction<typeof apiRoot>;
         
        (mockedAxiosInstance.post as jest.Mock).mockRejectedValueOnce(new AxiosError("Unauthorized","401",{},{},
          {
            data: { errors: [{ detail: 'a' }] },
            status:401,
            statusText:'Unauthorized',
            headers:{},
            config:{}
          }));

        const result= await GetToken({
            client_id:process.env.APPLICATION_ID,
            client_secret:process.env.APPLICATION_SECRET,
            code:"some-code",
            grant_type:"authorization_code"
        });
        
        expect(result).toEqual(response);
    })
})

Option-2 : Mock Service Worker

Don't mess with Axios

install packages

npm install msw --save-dev

getToken.test.ts

import { rest } from 'msw'
import { setupServer } from 'msw/node'
import GetToken from '@App/square-api/oauth/getToken';

const mockResponse =  { errors: [{ detail: 'a' }] } ;

const response = {
    error: true,
    module: 'getToken.ts',
    method: 'GetToken',
    message: 'a'
};

const server = setupServer(

// Url should be same with post request in getToken.ts 
rest.post("someURL", (req, res, ctx) => {
        return res(
            ctx.set('Content-Type', 'application/json'),
            ctx.status(401),
            ctx.json({...mockResponse})
        )
  })
)

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

describe('Token from OAuth', () => {
    test('Return error for expired code', async () => {
        const result= await GetToken({
            client_id:process.env.APPLICATION_ID,
            client_secret:process.env.APPLICATION_SECRET,
            code:"some-code",
            grant_type:"authorization_code"
        });
        
        expect(result).toEqual(response);
    })
})
  • Related