简体   繁体   English

在 TypeScript 中使用 proxyquire 和 sinon 存根 adm-zip

[英]Stub adm-zip with proxyquire and sinon in TypeScript

I want to unit test the class zip.adapter.ts with jest.我想用玩笑对类zip.adapter.ts进行单元测试。 I tried a lot of different methods to mock/stub the adm-zip package but nothing works.我尝试了很多不同的方法来模拟/存根adm-zip包,但没有任何效果。

I first tried ts-mock-imports but it always fails if I try to mock adm-zip.我首先尝试了ts-mock-imports,但如果我尝试模拟 adm-zip,它总是失败。 Then I tried sinon but it either failed to stub adm-zip or it just didn't stub it.然后我尝试了sinon,但它要么无法存根 adm-zip,要么就是没有存根。 My last resort was to combine sinon with proxyquire but that doesn't seem to work either....我最后的手段是将 sinon 与proxyquire结合使用,但这似乎也不起作用......

Has someone an idea why this doesn't work?有人知道为什么这不起作用吗? When the test calls the unzip method the code in it still uses the real adm-zip implementation...当测试调用unzip方法时,其中的代码仍然使用真正的 adm-zip 实现...

(I know the unit test doesn't make much sense because everything is mocked but I'm required to do it because of test coverage rules that I cannot change) (我知道单元测试没有多大意义,因为一切都是模拟的,但由于我无法更改的测试覆盖规则,我必须这样做)

zip.adapter.ts zip.adapter.ts

import * as admZip from 'adm-zip';

export class ZipAdapter {
    constructor() {}

    unzip(zip: Buffer, path: string) {
        const unzip = new admZip(zip);
        unzip.extractAllTo(path, true);
    }
}

zip.adapter.spec.ts zip.adapter.spec.ts

import * as sinon from 'sinon';
import { ZipAdapter } from './zip.adapter';
import * as proxyquire from 'proxyquire';

describe('Zip Adapter', () => {
    let zipAdapter: ZipAdapter;

    beforeEach(() => {
        const admZipInstance = { extractAllTo: sinon.stub() };
        const admZipStub = sinon.stub().callsFake(() => admZipInstance);
        const moduleStub = proxyquire('./zip.adapter.ts', { 'adm-zip': admZipStub });

        zipAdapter = new moduleStub.ZipAdapter();
    });

    it('should be defined', () => {
        expect(zipAdapter).toBeDefined();
    });

    it('should have called extractAllTo', () => {
        zipAdapter.unzip(Buffer.from(''), 'test');
    });
});

Update:更新:

I got my test working with Jest but only if I require() my module.我让我的测试与 Jest 一起工作,但前提是我需要()我的模块。 If I use my import without the require() the mock doesn't work.如果我在没有 require() 的情况下使用我的导入,则模拟不起作用。 Is it possible to get rid the require() and only use the import?是否可以摆脱 require() 而只使用导入?

import { ZipAdapter } from './zip.adapter';

describe('Zip Adapter', () => {
    let zipAdapter: ZipAdapter;
    let admZipExtractAllMock: jest.Mock<any, any>;

    beforeEach(() => {
        const admZipMock = jest.fn();
        admZipExtractAllMock = jest.fn();
        admZipMock.mockImplementation(() => {
            return { extractAllTo: admZipExtractAllMock };
        });
        jest.mock('adm-zip', () => admZipMock);

        const zipAdapterModule = require('./zip.adapter');
        zipAdapter = new zipAdapterModule.ZipAdapter();
    });

    it('should be defined', () => {
        expect(zipAdapter).toBeDefined();
    });

    it('should have called extractAllTo', () => {
        zipAdapter.unzip('unit', 'test');
        expect(admZipExtractAllMock.mock.calls.length).toBe(1);
    });
});

Top-level import only imports ZipAdapter type so it's evaluated the first time when it's imported with require .顶级导入仅导入ZipAdapter类型,因此在第一次使用require导入时会对其进行评估。 If it were mocked with jest.mock after the import, this couldn't affect imported module.如果在导入后用jest.mock它,这不会影响导入的模块。

If it needs to be mocked for all tests, it should be mocked and imported at top level:如果需要为所有测试模拟它,则应在顶级模拟和导入它:

import * as zipAdapterModule from './zip.adapter';

jest.mock('adm-zip', () => {
  let admZipExtractAllMock = jest.fn();
  return {
    __esModule: true,
    admZipExtractAllMock,
    default: jest.fn(() => ({ extractAllTo: admZipExtractAllMock }))
});

jest.mock at top level is hoisted above import . jest.mock被提升到import之上。 admZipExtractAllMock spy is exposed as named export in order to be able to change the implementation any time, preferable with Once methods to not affect other tests. admZipExtractAllMock间谍公开为命名导出,以便能够随时更改实现,最好使用Once方法不影响其他测试。

If it needs to not be mocked for some tests or Jest spy API is not enough to change the implementation, it needs to be mocked with jest.mock and imported with require inside a test like shown in the OP.如果不需要为某些测试jest.mock它或者 Jest spy API 不足以改变实现,则需要使用jest.mock并在 OP 中所示的测试中使用require导入。 In this case jest.resetModules should be added to allow mocked module to be re-imported.在这种情况下,应该添加jest.resetModules以允许重新导入模拟模块。

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

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