如何使用 Jest 监控 ES6 方法调用?

How to use Jest to monitor ES6 method calls?

正在使用打字稿测试调用实用程序 class 的 class。目标是模拟实用程序 class 并测试传递给调用的数据。

一个class待测试

import {Utils} from "~/src/play/utils";

export class Actions {
  constructor() {
}

async readFile(): Promise<void> {
   const utils = new Utils();
   await utils.action(5)
 }
}

const actions = new Actions();
actions.readFile()

实用程序class

export class Utils {
  constructor() {}

  async action(count: number): Promise<boolean> {
    console.log(count);
    return Promise.resolve(true);
  }
}

测试脚本

// @ts-ignore
import {Utils} from "./utils";
import {Actions} from "~/src/play/actions";

const mockUtils = jest.fn();
jest.mock('../utils', () => {
  return {
    Utils: jest.fn().mockImplementation(() => {
        return {
            default: () => {return {utils: mockUtils}},
            action: () => {
                return 5;
            }
        }
    })
   }
})

describe('actions', () => {
  beforeEach(() => {
    //  Utils.mockClear();
    mockUtils.mockClear();
  })

  test('success', async () => {
    const actions = new Actions()
    await actions.readFile()
    expect(mockUtils).toHaveBeenCalledWith(2)
  })
 })

它似乎没有接受模拟。结果是

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

Expected: 2

Number of calls: 0

at Object.<anonymous> (/Users/jrobens/NetBeansProjects/azuron/winpay-uploader/src/play/__test__/actions.spec.ts:28:27)
at processTicksAndRejections (internal/process/task_queues.js:95:5)

采纳@Stanislav Šolc 的建议添加 return 和参数值,包括 async

// @ts-ignore
import {Utils} from "../utils";
import {Actions} from "../actions";

const mockUtils = jest.fn();
jest.mock('../utils', () => {
return {
    Utils: jest.fn().mockImplementation(() => {
        return {
            default: () => {return {utils: mockUtils}},
             action: (count: number): Promise<boolean> => {
                 console.log(count);
                 return Promise.resolve(true);
            }
        }
    })
}
})

结果不变

jest.mock() 中的模块路径必须与用于在测试单元中加载依赖项的路径完全相同。顺便说一句,这就是测试与测试单元位于同一目录中的原因(您可能不会这样做)。

并且您必须模拟模块的确切结构,当然还有方法的异步性质。因此,在您的情况下,您使用异步操作方法导出构造函数。所以这样的事情是更好的开始。

jest.mock('~/src/play/utils', () => {
   return {
      Utils: jest.fn().mockImplementation(() => {
          return {action: jest.fn().mockResolvedValue(5)};
      }
   }
});

您可以使用 jest.spyOn(object, methodName) to mock Utils.prototype.action() method. Use jest.restoreAllMocks() 将所有模拟恢复到 afterEach 挂钩中的原始值。防止mock的值影响其他测试用例。

例如

actions.ts:

import { Utils } from './utils';

export class Actions {
  constructor() {}

  async readFile(): Promise<void> {
    const utils = new Utils();
    await utils.action(5);
  }
}

utils.ts:

export class Utils {
  constructor() {}

  async action(count: number): Promise<boolean> {
    console.log(count);
    return Promise.resolve(true);
  }
}

actions.test.ts:

import { Utils } from './utils';
import { Actions } from './actions';

describe('actions', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });

  test('success', async () => {
    const actionSpy = jest.spyOn(Utils.prototype, 'action').mockResolvedValueOnce(false);
    const actions = new Actions();
    await actions.readFile();
    expect(actionSpy).toHaveBeenCalledWith(5);
  });
});

测试结果:

 PASS  examples/68879782/actions.test.ts (10.186 s)
  actions
    ✓ success (3 ms)

------------|---------|----------|---------|---------|-------------------
File        | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------|---------|----------|---------|---------|-------------------
All files   |   71.43 |      100 |      75 |   71.43 |                   
 actions.ts |     100 |      100 |     100 |     100 |                   
 utils.ts   |   33.33 |      100 |      50 |   33.33 | 5-6               
------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.827 s, estimated 11 s