Search code examples
typescriptts-jest

How to test a function that return void with Jest? Tyepscript


I have some function and a type in file retry.ts

export const retryNTimes =  (retryFunction: Function) => {
    let retry = 0;
    while(retry < MAX_NUMBER_OF_RETRIES){
        try {
            retryFunction()
            break
        } catch (err) {
            console.log(`Try ${retry}: ${err}`)
            retry += 1
        }
    }
} 

type User = {
  userId: string
}

const TestUser = async ({
  userId,
}:User) => {
  console.log(`userid : ${userId} `)
}

I want to test test to make sure the function retryNTimes works.Then test to make sure it calls a function up to 3 times upon its failure. So I created a little TestUser function to do this with.

In file retry.test.ts,

describe('retry Function',() => {

test('retryNTimes function was call', () => {
    retryNTimes(function(){ TestUser()})
    expect(retryNTimes).toHaveBeenCalledTimes(1)
})

test('retry function to try 1 time', () => {
    retryNTimes(function(){ TestUser()})
    expect(TestUser).toHaveBeenCalledTimes(1)
})

 test('retry function to try 2 times', () => {
     retryNTimes(function(){ TestUser()})
     expect(TestUser).toHaveBeenCalledTimes(2)
 })

 test('retry function to try 3 times', () => {
     retryNTimes(function(){ TestUser()})
     expect(TestUser).toHaveBeenCalledTimes(3)
 })

})

However I recieve this error for the first 2 tests.

Matcher error: received value must be a mock or spy function

    Received has type:  function
    Received has value: [Function anonymous]

       9 | test('retryNTimes function was call', () => {
      10 |     retryNTimes(function(){ TestUser()})
    > 11 |     expect(retryNTimes).toHaveBeenCalledTimes(1)
         |                         ^
      12 | })

And I know the second two wont work with this set up because Im pretty sure I need to mock the function catching an error before it calls it again.

Ive been pretty bad with jest so far, so any help with setting up these tests is greatly appreciated.


Solution

  • You want to use a mock function:

    describe('retry Function',() => {
    
      test('retry function to try 1 time', async () => {
        // create a mock function that rejects once, then resolves
        const func = jest.fn()
                .mockRejectedValueOnce(new Error('intentional test error)')
                .mockResolvedValueOnce(void 0);
        await retryNTimes(func)
        expect(func).toHaveBeenCalledTimes(2)
      })
    })
    

    Note that you've got some other issues, primarily that your retry function doesn't handle async functions. You'll need to await to handle that:

    export const retryNTimes =  async (retryFunction: Function) => {
        let retry = 0;
        while(retry < MAX_NUMBER_OF_RETRIES){
            try {
                await retryFunction()
                break
            } catch (err) {
                console.log(`Try ${retry}: ${err}`)
                retry += 1
            }
        }
    }