检查通过 rewire 可见的(私有/非导出)函数上的方法调用

Posted

技术标签:

【中文标题】检查通过 rewire 可见的(私有/非导出)函数上的方法调用【英文标题】:Check method calls on (private/ non exported) function made visible by rewire 【发布时间】:2019-06-21 16:17:05 【问题描述】:

我有一个只有一个导出函数和几个非导出函数的模块来保持 api“干净”。我正在尝试使用 jest & require 编写单元测试,但出现以下错误:

Error: expect(jest.fn())[.not].toBeCalledTimes()
    jest.fn() value must be a mock function or spy.
Received:
    function: [Function doMagic]

如果我尝试进行间谍活动,我如何才能获得通过rewire (或其他验证方法被调用频率的其他方式)可见的非导出方法的监视,尽管能够在最后一次测试中调用函数如下所示:Cannot spy the doMagic property because it is not a function; undefined given instead

我的场景的简单示例: 例如functions.js

const moreFunctions = require("./moreFunctions");

const theExportedFunction = someNumber => 
  return doMagic(someNumber);
;

function doMagic(someNumber) 
  if (someNumber % 2 === 0) 
    return moreFunctions.getTrue();
  
  return moreFunctions.getFalse();


module.exports =  theExportedFunction ;

其他模块: moreFunctions.js

const moreFunctions = 
  getTrue: () => true,
  getFalse: () => false
;

module.exports = moreFunctions;

我是如何测试它的: functions.test.js

const rewire = require("rewire");

const functions = rewire("./functions");
const moreFunctions = functions.__get__('moreFunctions');

const doMagic = functions.__get__('doMagic');

const getFalse = jest.spyOn(moreFunctions, 'getFalse');
const getTrue = jest.spyOn(moreFunctions, 'getTrue');

describe("testing inner functions ", () => 

  afterEach(() => 
    jest.clearAllMocks();
  );

  test('theExportedFunction calls doMagic with 1 returns false and does not call getTrue', () => 
    const result = functions.theExportedFunction(1);
    console.log('result: ' + result);
    expect(result).toBe(false);

    //expect(doMagic).toBeCalledTimes(1);  // this blows up
    expect(getTrue).toHaveBeenCalledTimes(0);
    expect(getFalse).toHaveBeenCalledTimes(1);
  );

  test('theExportedFunction calls doMagic with 2 returns true and does not call getFalse', () => 
    const result = functions.theExportedFunction(2);
    console.log('result: ' + result);
    expect(result).toBe(true);

    //expect(doMagic).toBeCalledTimes(1); // this blows up
    expect(getTrue).toHaveBeenCalledTimes(1);
    expect(getFalse).toHaveBeenCalledTimes(0);
  );

  // This works!
  test('just testing to see if i can call the doMagic function', () => 
    const result = doMagic(2);
    expect(result).toBe(true);

    expect(getTrue).toHaveBeenCalledTimes(1);
    expect(getFalse).toHaveBeenCalledTimes(0);
  );
);

【问题讨论】:

【参考方案1】:

根据您的代码,您应该能够模拟所有并测试theExportedFunction,如下所示:

const mockGetTrue = jest.fn().mockReturnValue(true); // this name MUST start with mock prefix
const mockGetFalse = jest.fn().mockReturnValue(false); // this name MUST start with mock prefix
jest.spyOn('my_more_functions_path', () => (
  getTrue: mockGetTrue,
  getFalse: mockGetFalse
));
// here import the file you are testing, after the mocks! 
// for example
import theExportedFunction from 'my_path';

describe('theExportedFunction', () => 
  it('should call getTrue of moreFunctions', () => 
    theExportedFunction(4);
    expect(mockGetTrue).toHaveBeenCalled();
    expect(mockGetFalse).not.toHaveBeenCalled();
  );


  it('should call getFalse of moreFunctions', () => 
    theExportedFunction(5);
    expect(mockGetFalse).toHaveBeenCalled();
    expect(mockGetTrue).not.toHaveBeenCalled();
  );
);

【讨论】:

感谢@quirimmo,这是一种更优雅的测试方式。我在验证时遇到的问题是:expect(doMagic).toBeCalledTimes(1) 我在前两个测试中注释掉的那一行,因为我无法窥探它......但由于某种原因,我可以称之为它,正如你在最后看到的那样测试。 在你的情况下,问题应该是:expect(moreFunctions.getTrue).toHaveBeenCalledTimes(1);。看,我没有使用变量,而是直接使用方法。当你这样mock的时候,方法已经被mock了,你可以直接spy到方法 您知道一种方法可以检查中间方法doMagic 是否已被调用,最好是toBeCalledWith() 或类似的方法,以便我也可以验证参数?这只是一个帮助我解释问题的简单示例

以上是关于检查通过 rewire 可见的(私有/非导出)函数上的方法调用的主要内容,如果未能解决你的问题,请参考以下文章

使用通用测试的模块的 Erlang 测试(非导出/私有)功能

NPM测试模块之rewire教程

访问可见性问题和@property装饰器

为自定义私有类实现 Equatable - Swift

私人包裹的处理方式是啥?

Java 私有构造函数可见性