【问题标题】:How to spy on a default exported function with Jest?如何使用 Jest 监视默认导出函数?
【发布时间】:2019-06-12 05:27:23
【问题描述】:

假设我有一个导出默认函数的简单文件:

// UniqueIdGenerator.js
const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);

export default uniqueIdGenerator;

我会这样使用:

import uniqueIdGenerator from './UniqueIdGenerator';
// ...
uniqueIdGenerator();

我想在我的测试中断言这个方法是在保持原始功能的同时被调用的。我会用jest.spyOn 来做这件事,但是它需要一个对象和一个函数名作为参数。你怎么能以干净的方式做到这一点?对于任何感兴趣的人,jasmine 都有类似的 GitHub issue

【问题讨论】:

    标签: reactjs unit-testing mocking jestjs spy


    【解决方案1】:

    这是一种在不修改导入(甚至根本不需要在测试中导入)的情况下进行默认导出的方法:

    const actual = jest.requireActual("./UniqueIdGenerator");
    const spy = jest.spyOn(actual, "default");
    

    【讨论】:

    • 对我来说最干净的答案
    【解决方案2】:

    仅模拟默认导出或任何其他导出,但将模块中的剩余导出保留为原始:

    import myDefault, { myFunc, notMocked } from "./myModule";
    
    jest.mock("./myModule", () => {
      const original = jest.requireActual("./myModule");
      return {
        __esModule: true,
        ...original,
        default: jest.fn(),
        myFunc: jest.fn()
      }
    });
    
    describe('my description', () => {
      it('my test', () => {
        myFunc();
        myDefault();
        expect(myFunct).toHaveBeenCalled();
        expect(myDefault).toHaveBeenCalled();
        
        myDefault.mockImplementation(() => 5);
        expect(myDefault()).toBe(5);
        expect(notMocked()).toBe("i'm not mocked!");
      })
    });
    

    【讨论】:

    • 漂亮的答案,很有帮助。
    • 非常好!就个人而言,我仍然需要让默认方法返回其原始值,所以我在 jest.fn() 中调用它,例如:default: jest.fn((...args) => original(...args))
    【解决方案3】:

    对我有用的是Janne Annala 的答案和 OP 自己的解决方案的结合。我想测试的是,辅助方法是用正确的参数调用的,因为我已经为辅助方法编写了一个测试,它对我的​​后续测试没有任何影响:

    // myHelperMethod.js
    
    export const myHelperMethod = (param1, param2) => { // do something with the params };
    
    // someOtherFileUsingMyHelperMethod.js
    
    import * as MyHelperMethod from '../myHelperMethod';
    
    
    jest.mock('../myHelperMethod', () => ({
      myHelperMethod: jest.fn(),
    }));
    
    let myHelperMethodSpy = jest.spyOn(MyHelperMethod, 'myHelperMethod');
    
    // ...
    // some setup
    // ...
    
    test(() => {
      expect(myHelperMethodSpy).toHaveBeenCalledWith(param1, param2);
    });
    

    【讨论】:

    • 投反对票,因为它没有解决原始问题的关键 - 如何使用默认导出来做到这一点
    • 尽管 OP 自己选择在自己接受的答案中放弃默认导出?
    【解决方案4】:

    也可以模拟导入并将原始实现作为模拟实现传递,例如:

    import uniqueIdGenerator from './UniqueIdGenerator'; // this import is a mock already
    
    jest.mock('./UniqueIdGenerator.js', () => {
      const original = jest. requireActual('./UniqueIdGenerator')
      return {
         __esModule: true,
         default: jest.fn(original.default)
      }
    })
    
    test(() => {
      expect(uniqueIdGenerator).toHaveBeenCalled()
    })
    

    【讨论】:

    • 这应该是公认的答案
    • 实际上感谢 jest 设置 resetMocks:true 现在默认情况下,这仅适用于第一次测试。之后default: jest.fn 被重置并变为undefined,所以在现代笑话中使用默认配置,如果没有其他解决方法,这将不再有效
    【解决方案5】:

    在某些情况下,您必须模拟导入才能窥探默认导出:

    import * as fetch from 'node-fetch'
    
    jest.mock('node-fetch', () => ({
      default: jest.fn(),
    }))
    
    jest.spyOn(fetch, 'default')
    

    【讨论】:

      【解决方案6】:

      我最终放弃了默认导出:

      // UniqueIdGenerator.js
      export const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
      

      然后我可以像这样使用和监视它:

      import * as UniqueIdGenerator from './UniqueIdGenerator';
      // ...
      const spy = jest.spyOn(UniqueIdGenerator, 'uniqueIdGenerator');
      

      Some recommend 将它们包装在一个 const 对象中,然后将其导出。我想你也可以使用一个类来包装。

      但是,如果您不能修改类,仍然有一个(不太好的)解决方案:

      import * as UniqueIdGenerator from './UniqueIdGenerator';
      // ...
      const spy = jest.spyOn(UniqueIdGenerator, 'default');
      

      【讨论】:

      • 但这只有在 babel 通过 es6 模块转译时才有效。不适用于 CommonJS
      猜你喜欢
      • 1970-01-01
      • 2015-12-29
      • 2021-04-12
      • 2016-03-10
      • 1970-01-01
      • 2020-02-03
      • 2019-07-27
      • 2019-02-15
      • 2021-06-28
      相关资源
      最近更新 更多