4

我想测试一个函数,它只返回一个带有一些值的函数和一个匿名函数作为参数。如何在玩笑中使用 toHaveBeenCalledWith 测试匿名函数的匹配?

function toBeTested( id, values) {
 return xyz(id, values, () => {
  return { 
    type: 'foo', 
    payload: { 
     text: values.text
    }
  }
 })
}

在我的测试中

describe('test for toBeTested',  () => {
  it('should call xyz with params', () => {
    const id = 123;
    const values = {
      text: 'Hello world',
    };

   xyz = jest.fn();
   toBeTested(id, values);
    expect(xyz).toHaveBeenCalledWith(id, values, () => {
     return {
      type: 'foo',
      payload: {
       text: values.text,
      }
     }
    });
  })
})

测试错误报告

 expect(jest.fn()).toHaveBeenCalledWith(expected)

    Expected mock function to have been called with:
      [123, {text: 'Hello world'}, [Function anonymous]]
    But it was called with:
      [123, {text: 'Hello world'}, [Function anonymous]]

      at Object.it.only (src/actions/tests/xyz.test.js:30:43)
          at new Promise (<anonymous>)
      at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
          at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)
4

2 回答 2

6

我有同样的问题,我发现这个问题选择的答案对我没有帮助。所以我决定写一个解决我问题的答案,希望它可以帮助其他人。

TL;DR:使用expect.any(Function)如下:

    expect(xyz).toHaveBeenCalledWith(
      id,
      values,
      expect.any(Function)
    );

完整答案:

对于可以接收回调的情况,选择的答案确实是一种替代方案,但是在其他情况下,匿名函数不应该公开,甚至不应该由调用者函数提供。

例子:

import { exec } from 'child_process';

export const runScript = (data: string): Promise<string> => {
  return new Promise((resolve, reject) => {
    exec(`a script to be called with this data ${data}`, (error) => {
      if (error) {
        // handle error
        // ...
        reject();
      } else {
        // do something
        // ...
        resolve('result');
      }
    });
  });
};

在上面的示例中,我不想将错误处理函数作为回调接收,因为该逻辑实际上是函数固有的runScript,不应由外部调用者函数实现或提供。真正的功能会有更多的逻辑,但我相信分享它的结构已经给出了这些场景存在的想法。

注意:// ...我的代码中的部分有更多的逻辑,调用外部函数,我可以模拟和断言传递给它们的内容,所以我可以正确地测试它。我只是为了简化而删除了它

无论如何,这是我设法测试它的方法:

expect(execMock).toHaveBeenCalledWith(
  '...',
  expect.any(Function),
);

在您的情况下@manikandan-j,它将是:


it('should call xyz with params', () => {
    const id = 123;
    const values = {
      text: 'Hello world',
    };

    xyz = jest.fn();
    toBeTested(id, values);
    expect(xyz).toHaveBeenCalledWith(
      id,
      values,
      expect.any(Function)
    );
  });
});

此外,如果您使用expect.any(Function)(正如我正在使用的那样)并且想要测试匿名函数的内容

() => {
  return { 
    type: 'foo', 
    payload: { 
    text: values.text
  }
}

您将测试结果toBeTested会受到这些值的影响。如下所示:

    const result = toBeTested(id, values);
    expect(xyz).toHaveBeenCalledWith(
      id,
      values,
      expect.any(Function)
    );
    expect(result).toBe(...);

无论如何,我希望它可以帮助人们测试其参数之一是不能作为参数接收的匿名函数。

于 2021-02-23T02:09:04.263 回答
-3

您的代码不容易测试。你最好像这样重构它们:

function toBeTested(id, values, xyz, callback) {
  return xyz(id, values, callback);
}

export { toBeTested };

单元测试:

import { toBeTested } from './toBeTested';

describe('toBeTested', () => {
  it('t1', () => {
    const id = 123;
    const values = {
      text: 'Hello world'
    };

    const mockCallback = jest.fn(vals => {
      return {
        type: 'foo',
        payload: {
          text: vals.text
        }
      };
    });

    const mockXyz = jest.fn();

    toBeTested(id, values, mockXyz, mockCallback);
    expect(mockXyz).toBeCalledWith(id, values, mockCallback);
    expect(mockXyz.mock.calls.length).toBe(1);
    expect(mockXyz.mock.calls[0][0]).toBe(id);
    expect(mockXyz.mock.calls[0][1]).toBe(values);
    expect(mockXyz.mock.calls[0][2]).toBe(mockCallback);
  });
});

单元测试结果:

 PASS  src/mock-function/callback/index.spec.ts
  toBeTested
    ✓ t1 (10ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.795s, estimated 3s
于 2019-08-20T17:32:38.387 回答