如何使用toHaveBeenCalledWith检查作为匿名函数的函数参数?

时间:2019-08-15 14:09:01

标签: jestjs enzyme

我想测试一个仅返回带有一些值的函数和一个匿名函数作为参数的函数。如何在玩笑中使用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)

2 个答案:

答案 0 :(得分:1)

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

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(...);

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

答案 1 :(得分:0)

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

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