繁体   English   中英

如何使用 Jest 模拟 ES6 模块导入?

[英]How can I mock an ES6 module import using Jest?

我想测试我的一个ES6模块是否以特定方式调用另一个 ES6 模块。 有了茉莉花,这非常容易——

申请代码:

// myModule.js
import dependency from './dependency';

export default (x) => {
  dependency.doSomething(x * 2);
}

和测试代码:

//myModule-test.js
import myModule from '../myModule';
import dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    spyOn(dependency, 'doSomething');

    myModule(2);

    expect(dependency.doSomething).toHaveBeenCalledWith(4);
  });
});

Jest 的等价物是什么? 我觉得这是一件很简单的事情,但我一直在努力解决这个问题。

我最接近的方法是将import替换为require ,并将它们移动到测试/函数中。 这两件事都不是我想做的。

// myModule.js
export default (x) => {
  const dependency = require('./dependency'); // Yuck
  dependency.doSomething(x * 2);
}

//myModule-test.js
describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    jest.mock('../dependency');

    myModule(2);

    const dependency = require('../dependency'); // Also yuck
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

对于奖励积分,当dependency.js中的函数是默认导出时,我很乐意使整个工作正常进行。 但是,我知道监视默认导出在 Jasmine 中不起作用(或者至少我永远无法让它起作用),所以我也不希望它在 Jest 中是可能的。

编辑:几年过去了,这不再是正确的方法了(可能从来都不是,我的错)。

对导入的模块进行变异是令人讨厌的,并且可能会导致副作用,例如根据执行顺序通过或失败的测试。

出于历史目的,我将这个答案保留为原始形式,但你真的应该使用jest.spyOnjest.mock 有关详细信息,请参阅本页上的笑话文档或其他答案。

原答案如下:


我已经能够通过使用涉及import *的破解来解决这个问题。 它甚至适用于命名和默认导出!

对于命名导出:

// dependency.js
export const doSomething = (y) => console.log(y)
// myModule.js
import { doSomething } from './dependency';

export default (x) => {
  doSomething(x * 2);
}
// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.doSomething = jest.fn(); // Mutate the named export

    myModule(2);

    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

或者对于默认导出:

// dependency.js
export default (y) => console.log(y)
// myModule.js
import dependency from './dependency'; // Note lack of curlies

export default (x) => {
  dependency(x * 2);
}
// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.default = jest.fn(); // Mutate the default export

    myModule(2);

    expect(dependency.default).toBeCalledWith(4); // Assert against the default
  });
});

您必须自己模拟模块并设置间谍:

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency', () => ({
  doSomething: jest.fn()
}))

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

快进到 2020 年,我发现这篇博文是解决方案: Jest mock default and named export

仅使用 ES6 模块语法:

// esModule.js
export default 'defaultExport';
export const namedExport = () => {};

// esModule.test.js
jest.mock('./esModule', () => ({
  __esModule: true, // this property makes it work
  default: 'mockedDefaultExport',
  namedExport: jest.fn(),
}));

import defaultExport, { namedExport } from './esModule';
defaultExport; // 'mockedDefaultExport'
namedExport; // mock function

您还需要知道的一件事(我花了一段时间才弄清楚)是您不能在测试中调用 jest.mock() ; 您必须在模块的顶层调用它。 但是,如果您想为不同的测试设置不同的模拟,您可以在单个测试中调用 mockImplementation()。

使用 Jest 模拟 ES6 依赖模块默认导出:

import myModule from '../myModule';
import dependency from '../dependency';

jest.mock('../dependency');

// If necessary, you can place a mock implementation like this:
dependency.mockImplementation(() => 42);

describe('myModule', () => {
  it('calls the dependency once with double the input', () => {
    myModule(2);

    expect(dependency).toHaveBeenCalledTimes(1);
    expect(dependency).toHaveBeenCalledWith(4);
  });
});

其他选项不适用于我的情况。

Andreas 的回答添加更多内容。 我对 ES6 代码也有同样的问题,但我不想改变导入。 那看起来很老套。 所以我这样做了:

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
  });
});

并在与文件dependency.js平行的“__ mocks __”文件夹中添加了文件dependency.js 这对我有用。 此外,这让我可以选择从模拟实现中返回合适的数据。 确保为要模拟的模块提供正确的路径。

这个问题已经回答了,但你可以像这样解决它:

文件依赖.js

const doSomething = (x) => x
export default doSomething;

文件myModule.js

import doSomething from "./dependency";

export default (x) => doSomething(x * 2);

文件myModule.spec.js

jest.mock('../dependency');
import doSomething from "../dependency";
import myModule from "../myModule";

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    doSomething.mockImplementation((x) => x * 10)

    myModule(2);

    expect(doSomething).toHaveBeenCalledWith(4);
    console.log(myModule(2)) // 40
  });
});

我用另一种方式解决了这个问题。 假设你有你的dependency.js

export const myFunction = () => { }

我创建了一个 depdency.mock.js 文件,其中包含以下内容:

export const mockFunction = jest.fn();

jest.mock('dependency.js', () => ({ myFunction: mockFunction }));

在测试中,在导入具有依赖项的文件之前,我使用:

import { mockFunction } from 'dependency.mock'
import functionThatCallsDep from './tested-code'

it('my test', () => {
    mockFunction.returnValue(false);

    functionThatCallsDep();

    expect(mockFunction).toHaveBeenCalled();

})

这里的答案似乎都对我不起作用(原始函数总是被导入而不是模拟),而且 Jest 中的 ESM 支持似乎仍在进行中

发现这条评论后,我发现jest.mock()实际上不适用于常规导入,因为导入总是在模拟之前运行。 因此,我正在使用await import()导入我的依赖项。 这甚至适用于顶级等待,所以我只需要调整我的导入:

import { describe, expect, it, jest } from '@jest/globals';

jest.mock('../dependency', () => ({
  doSomething: jest.fn()
}));

const myModule = await import('../myModule');
const dependency = await import('../dependency');

describe('myModule', async () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

我想测试我的一个ES6模块调用特定方式的另一个ES6模块。 有了茉莉花,这非常容易-

应用代码:

// myModule.js
import dependency from './dependency';

export default (x) => {
  dependency.doSomething(x * 2);
}

和测试代码:

//myModule-test.js
import myModule from '../myModule';
import dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    spyOn(dependency, 'doSomething');

    myModule(2);

    expect(dependency.doSomething).toHaveBeenCalledWith(4);
  });
});

笑话相当于什么? 我觉得这是一件很想做的简单的事,但是我一直在努力尝试弄清头发。

我最接近的方法是将import替换为require ,然后将它们移入测试/函数中。 都不是我想做的事情。

// myModule.js
export default (x) => {
  const dependency = require('./dependency'); // Yuck
  dependency.doSomething(x * 2);
}

//myModule-test.js
describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    jest.mock('../dependency');

    myModule(2);

    const dependency = require('../dependency'); // Also yuck
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

为了获得加分,我很想当dependency.js的函数为默认导出时,可以使整个工作正常进行。 但是,我知道监视默认出口在Jasmine中不起作用(或者至少我永远无法使它起作用),因此我也不希望在Jest中也有可能。

我尝试了所有解决方案,但没有一个有效或显示出很多 TS 错误。

这就是我解决它的方法:

format.ts文件:

import camelcaseKeys from 'camelcase-keys'
import parse from 'xml-parser'

class Format {
  parseXml (xml: string) {
    return camelcaseKeys(parse(xml), {
      deep: true,
    })
  }
}

const format = new Format()
export { format }

format.test.ts文件:

import camelcaseKeys from 'camelcase-keys'
import parse from 'xml-parser'

jest.mock('xml-parser', () => jest.fn().mockReturnValue('parsed'))
jest.mock('camelcase-keys', () => jest.fn().mockReturnValue('camel cased'))

describe('parseXml', () => {
  test('functions called', () => {
    const result = format.parseXml('XML')

    expect(parse).toHaveBeenCalledWith('XML')
    expect(camelcaseKeys).toHaveBeenCalledWith('parsed', { deep: true })
    expect(result).toBe('camel cased')
  })
})

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM