1

I have this function which calls an util function for api calls. The util function resolves or rejects based on the api result.

Now, I need to unit test the callback functions which has the following structure.

`theClassMethod : () => {
    return utilMethod().then(
    result => { this.functionOne() //Test this function is called }, 
    error => { this.functionTwo() //Test this function is called }
    )
}`

The util method returns a promise like below:

utilFunc = (data :string) :Promise<ResultData[]> => {
    return new Promise(async (resolve, reject) => {
        try{
            resolve(data)
        }catch{
            reject(error)
        }
    }) 
}

https://codesandbox.io/s/vjnwy1zw75?fontsize=14

What I tried:

  1. Mocked the util method to resolve/reject. Call the class method and do assertions. It doesn't work and the test always passes as a false positive.

I have spend much time looking for a similar problem. Most questions here are to test the code like:

theClassMethod : () => { utilMethod.then().catch()}

The problem I am trying to solve is to test the resolve, reject callbacks in the then block then(function1, function2). That the code block inside function1 has to be tested that it calls some intended functions.

halfer
  • 19,824
  • 17
  • 99
  • 186
ebiv
  • 315
  • 2
  • 8
  • does your test's code follow [jest guideline](https://jestjs.io/docs/en/asynchronous) for testing async code? – skyboyer Apr 25 '19 at 21:43
  • yes, it does. checked – ebiv Apr 26 '19 at 08:13
  • can you compose minimal example of how does your test look like? – skyboyer Apr 26 '19 at 08:51
  • https://codesandbox.io/s/vjnwy1zw75?fontsize=14. @skyboyer – ebiv Apr 26 '19 at 14:44
  • Please do not try to discourage duplicate question suggestions - readers will make them regardless. They are nearly always given with helpfulness, and it is ideal to be open to them, even if you believe you have searched thoroughly. You may have not seen a good duplicate, or you may have seen it but not understood why it is a duplicate. – halfer Apr 27 '19 at 15:24

1 Answers1

3

The approach you are describing (mocking utilMethod to resolve/reject) is a good approach.

Here is a simple working example to get you started:

Note: I implemented functionOne as a class method and functionTwo as an instance property to show how to spy on both types of functions:

util.js

export const utilMethod = async () => 'original';

code.js

import { utilMethod } from './util';

export class MyClass {
  functionOne() { }  // <= class method
  functionTwo = () => { }  // <= instance property
  theClassMethod() {
    return utilMethod().then(
      result => { this.functionOne() },
      error => { this.functionTwo() }
    );
  }
}

code.test.js

import { MyClass } from './code';
import * as util from './util';

test('theClassMethod', async () => {
  const mock = jest.spyOn(util, 'utilMethod');

  const instance = new MyClass();

  const functionOneSpy = jest.spyOn(MyClass.prototype, 'functionOne');  // <= class method
  const functionTwoSpy = jest.spyOn(instance, 'functionTwo');  // <= instance property

  mock.mockResolvedValue('mocked value');  // <= mock it to resolve
  await instance.theClassMethod();
  expect(functionOneSpy).toHaveBeenCalled();  // Success!

  mock.mockRejectedValue(new Error('something bad happened'));  // <= mock it to reject
  await instance.theClassMethod();
  expect(functionTwoSpy).toHaveBeenCalled();  // Success!
});
Brian Adams
  • 43,011
  • 9
  • 113
  • 111
  • Many thanks for taking the time to solve this. This solution is correct when the success callback just returns back the value, but my code is structured in a way that the success call back has a function inside which it calls. I am trying to test whether that function is called on success. It was my bad the question was not clear, i have updated the question now. Is there a way we could know whether the function inside success callback function block is called? – ebiv Apr 26 '19 at 08:13
  • I updated my answer to assert that functions are called. @ebiv – Brian Adams Apr 28 '19 at 04:51
  • I also took a look at the code on your sandbox...looks like you just need to make your test function `async` and call `await` on the `Promise` returned by `instance.callThirdPartyAPI()` to make sure it completes before asserting that your spy was called. @ebiv – Brian Adams Apr 28 '19 at 04:51