Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to test post methods with Jest due to error can not read mockImplementation of undefined

I have an api service where I have different methods to make calls to the APIs. I've successfully tested all of the GET requests but I am having troubles testing the POST requests.

This is the method:

export default class ApiService {
  static makeApiCall = <T>(
    url: string,
    oneCb: <T>(d: Data) => T,
    secondCb: (d: T) => void,
    errorCb?: (a: ErrorModel) => void,
    method = 'get',
    data = {},
  ): Promise<void> => {
    const config: AxiosRequestConfig = {};
    if (method === 'post') {
      config.headers = header;
      return ApiClient.post(url, data, config)
        .then(res => callback(normalizeCallback(res.data))).catch(error => someHandler(error));            
    } else {
      return ApiClient.get(url)
        .then(res => callback(normalizeCallback(res.data))).catch(error => someHandler(error));
    }
  };

  // ONLY ONE POST METHOD TO MAKE IT MORE CLEAR
  static someArchiveMethod = (
    callback: (a: SuccessModel) => void,
    errorCallback: (error: ErrorModel) => void,
    cardId: string
  ): Promise<void> => {
    return ApiService.makeApiCall<SuccessfulResponse>(
      'appreciationCard/archive',
      Normalizer.successfulResponse,
      callback,
      errorCallback,
      'post',
      { cardId }
    );
  };

  // HERE BELOW THE GET METHODS
  static getPeople = (cb: (a: PeopleModel[]) => void, page?: number, limit?: number): Promise<void> => {
    const queryDetails = { page, limit };
    return ApiService.makeApiCall<PeopleModel[]>(
      `people?${toQueryString(queryDetails)}`,
      Normalizer.normalizePeople,
      callback
    );
  };
};

This is how I am testing everything related to the GETs:

describe('apiService', () => {
  beforeAll(() => {
    expect(ApiClient.defaults.headers.common.Authorization).toBe('Bearer test token');
    // @ts-ignore
    ApiClient.get.mockImplementation((url: string) => {
      return Promise.resolve({ data: mockData });
    });
  });

  it('should call api client method', () => {
    ApiService.makeApiCall(
      'testUrl',
      data => data,
      res => res,
      err => err,
      'get'
    );

    expect(ApiClient.get).toBeCalledTimes(1);
    expect(ApiClient.get).toBeCalledWith('testUrl');
  });

  it('should call callbacks consequently', done => {
    ApiService.makeApiCall('testUrl', firstCallback, secondCallback).then(() => {
      expect(firstCallback).toBeCalledTimes(1);
      expect(firstCallback).toBeCalledWith(mockData);
      expect(secondCallback).toBeCalledTimes(1);
      expect(secondCallback).toBeCalledWith(firstCallback(mockData));
      done();
    });
  });
});

describe('api service error flow', () => {
  beforeAll(() => {
    // @ts-ignore
    ApiClient.get.mockImplementation((url: string) => {
      console.log('error result');
      return Promise.reject(mockError);
    });
  });

  it('should handle error', done => {
    console.error = jest.fn();

    const firstCallback = jest.fn((data: any) => data);
    const secondCallback = jest.fn((data: any) => data);

    ApiService.makeApiCall('testUrl', firstCallback, secondCallback).then(() => {
      expect(firstCallback).toBeCalledTimes(0);
      expect(secondCallback).toBeCalledTimes(0);
      expect(console.error).toBeCalledTimes(1);
      expect(console.error).toBeCalledWith('ApiClient testUrl', mockError);
      done();
    });
  });
});

describe('apiService methods', () => {
  beforeAll(() => {
    ApiClient.get.mockImplementation((url: string) => {
      expect(ApiClient.defaults.headers.common.Authorization).toBe('Bearer test token');

      return Promise.resolve({ data: mockData });
    });
  });

  it('getPeople method call with one param', () => {
    ApiService.getPeople(jest.fn(), 1, 1).then(() => {
      expect(ApiClient.get).toBeCalledWith('people?page=1&limit=1');
    });
  });
})

I thought that only by changing all of the instances of ApiClient.get to ApiClient.post it will work to test the POST requests. But when I attempt to do that it says that can not read mockImplementation of undefined. I tried changing the methods in the tests to use the post param in order to overwrite the param method = 'get' but I don't have any success, I get this error

TypeError: apiClient_1.default.post is not a function

Any thoughts?

like image 393
Reacting Avatar asked Jan 25 '20 00:01

Reacting


People also ask

How to mock an entire class in jest?

import service from './Service'; jest.mock ('./Service', () => jest.fn ()); service.yourFunction = jest.fn ( () => { /*your mock*/ }) Say your service.js is in javascript/utils, create a javascript/utils/_ mocks _ and inside it create a service.js file, you can then mock the entire class in this file, eg:

Why is my jest file not working?

Jest encountered an unexpected token This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript. By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

How to make jest ignore some of my node_modules?

By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules". Here's what you can do: • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config. • If you need a custom transformation specify a "transform" option in your config.

How can I mock my non-JS modules?

• If you need a custom transformation specify a "transform" option in your config. • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.


1 Answers

I've investigated your problem. First of all, I want to tell that your code has a couple of issues like calling callback that you didn't define, unclear defining of ApiClient etc.

So, I created a Repl example to reproduce your issue in which I simplified your code a bit but all the main elements are there.

Please, take a look https://repl.it/@SergeyMell/Some-Jesting

It works successfully for both get and post methods with no issues. Here, are the main points you should pay your attention on:

  1. Using axios as ApiClient. (It was not clear from your question so I assumed that it is so)
    const ApiClient = require('axios');
    
  2. Setting jest mocks on axios (Suppose, you do the same)
    jest.mock('axios');
    
  3. Putting mocks to both get and post requests in a similar manner (same to your way)

    ApiClient.get.mockImplementation((url) => {
      return Promise.resolve({ data: mockData });
    });
    
    ApiClient.post.mockImplementation((url) => {
      return Promise.resolve({ data: mockData });
    });
    

So, please, check my example, check the differences with your code and let me know regarding some additional detalization that you may need.

like image 94
Sergey Mell Avatar answered Oct 13 '22 16:10

Sergey Mell