首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用jest - Typescript对具有不同实现的node_module进行模拟

使用jest - Typescript对具有不同实现的node_module进行模拟
EN

Stack Overflow用户
提问于 2020-04-04 16:44:28
回答 1查看 297关注 0票数 0

我正在尝试用nodejs实现gcloud存储,并使用类型记录测试它们--这是我的实际类。

请暂时不要考虑日志记录实现。存储由外部服务调用进行身份验证-

const = GcloudAuthenticationInstance.createGcloudAuthenticationBucket();

而且,我愿意在gcloud中存储的文件是使用流操作的,使用的是泵模块。

代码语言:javascript
复制
export const uploadEnvFiles = async (env_name: string) => {

        const LOGGER: pino.Logger = PinoLoggerServiceInstance.getLogger(__filename);

        return new Promise(async (res, rej) => {
            const str = GcloudAuthenticationInstance.createGcloudAuthenticationBucket();

            const bucketToUpload = GCLOUD_ENV_STR_BUCKET_NAME;
            let uploadLocalFilePath;
            let destinationBucketPath;
            if (!AppUtilServiceInstance.isNullOrUndefined(env_name)) {
                uploadLocalFilePath = ENV_NAME_DEV === env_name ? GCLOUD_UPLOAD_FILE_DEV_LOCAL_PATH : GCLOUD_UPLOAD_FILE_PROD_LOCAL_PATH;
                destinationBucketPath = ENV_NAME_DEV === env_name ? GCLOUD_DATABASE_BUCKET_DEV : GCLOUD_DATABASE_BUCKET_PROD;
            }
            LOGGER.info('after authentication');
            pump(
                fs.createReadStream(uploadLocalFilePath),
                str
                    .bucket(bucketToUpload)
                    .file(destinationBucketPath)
                    .createWriteStream({
                        gzip: true,
                        public: true,
                        resumable: true,
                    })
            )
                .on('error', (err) => {
                    LOGGER.error('Error occured in uploading:', err);
                    rej({ status: 'Error', error: err, code: 500 });
                })
                .on('finish', () => {
                    LOGGER.info('Successfully uploaded the file');
                    res({ status: 'Success', code: 201, error: null });
                });
        });
    };

现在,有可能完成或错误的流,我想测试这两个。我能够模拟泵npm模块作为一个整体,在任何测试套件声明之前,jest.mock都会像这样悬挂在顶部。

代码语言:javascript
复制
jest.mock('pump', () =>
    jest.fn().mockImplementation(() => {
        const readStream = fs.createReadStream(
            path.resolve(process.cwd(), './tests/cloud-storage/sample-read.txt')
        );
        const writeStream = fs.createWriteStream(
            path.resolve(process.cwd(), './tests/cloud-storage/sample-write.txt')
        );
        return readStream.pipe(writeStream);
    })
);

因此,上面是工作场景的一个实现,在这个场景中,我将一个现有文件输送到一个输出流,并返回该流,使模拟泵工作。这是我的测试规范文件

代码语言:javascript
复制
const globalAny: any = global;

describe('Test suite for bucket functionality', () => {
    beforeEach(() => {
        jest.restoreAllMocks();

    });
    afterAll(() => {
        jest.clearAllMocks();
        jest.restoreAllMocks();
        jest.resetAllMocks();

    });

    test('test upload - make the actual call', async (done) => {
        // to make sure that mock fs doesnt affect the gcloud authentication, this is a MUST
        const createGcloudAuthenticationBucketSpy = jest
            .spyOn(GcloudAuthenticationInstance, 'createGcloudAuthenticationBucket')
            .mockImplementation(() => {
                return new Storage();
            });
        const res = BucketOperations.uploadEnvFiles(globalAny.ENV_JEST);
        await expect(res).resolves.toBeDefined();
        expect(createGcloudAuthenticationBucketSpy).toHaveBeenCalledTimes(1);
        done();
    });

});

现在,这适用于模拟的泵调用。但是我想测试流在同一规范中也会发出错误的场景。是否有可能在另一个测试规范中覆盖mockImplementation。由于这是一个npm模块,我已经在顶部编写了jest.mock(),它将作为整个测试套件的模拟,但不确定如何覆盖它。我已经试了三天了,但都想不出来。有什么办法可以做到吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-04-09 06:12:23

下面是使用jest.mock(moduleName,工厂,选项)jest.spyOn(object,methodName)的单元测试解决方案。

bucketOperations.ts

代码语言:javascript
复制
import fs from 'fs';
import pump from 'pump';
import { GcloudAuthenticationInstance } from './gcloudAuthenticationInstance';
import { AppUtilServiceInstance } from './appUtilServiceInstance';

const {
  GCLOUD_ENV_STR_BUCKET_NAME,
  GCLOUD_UPLOAD_FILE_DEV_LOCAL_PATH,
  GCLOUD_UPLOAD_FILE_PROD_LOCAL_PATH,
  GCLOUD_DATABASE_BUCKET_DEV,
  GCLOUD_DATABASE_BUCKET_PROD,
  ENV_NAME_DEV,
} = process.env;

export const uploadEnvFiles = async (env_name: string) => {
  return new Promise(async (res, rej) => {
    const str = GcloudAuthenticationInstance.createGcloudAuthenticationBucket();

    const bucketToUpload = GCLOUD_ENV_STR_BUCKET_NAME;
    let uploadLocalFilePath;
    let destinationBucketPath;
    if (!AppUtilServiceInstance.isNullOrUndefined(env_name)) {
      uploadLocalFilePath =
        ENV_NAME_DEV === env_name ? GCLOUD_UPLOAD_FILE_DEV_LOCAL_PATH : GCLOUD_UPLOAD_FILE_PROD_LOCAL_PATH;
      destinationBucketPath = ENV_NAME_DEV === env_name ? GCLOUD_DATABASE_BUCKET_DEV : GCLOUD_DATABASE_BUCKET_PROD;
    }
    console.info('after authentication');
    pump(
      fs.createReadStream(uploadLocalFilePath),
      str
        .bucket(bucketToUpload)
        .file(destinationBucketPath)
        .createWriteStream({
          gzip: true,
          public: true,
          resumable: true,
        }),
    )
      .on('error', (err) => {
        console.error('Error occured in uploading:', err);
        rej({ status: 'Error', error: err, code: 500 });
      })
      .on('finish', () => {
        console.info('Successfully uploaded the file');
        res({ status: 'Success', code: 201, error: null });
      });
  });
};

appUtilServiceInstance.ts

代码语言:javascript
复制
const AppUtilServiceInstance = {
  isNullOrUndefined: (env_name) => typeof env_name === 'undefined',
};

export { AppUtilServiceInstance };

gcloudAuthenticationInstance.ts

代码语言:javascript
复制
const GcloudAuthenticationInstance = {
  createGcloudAuthenticationBucket: () => {
    const storage = {
      bucket(name) {
        return this;
      },
      file(filename) {
        return this;
      },
      createWriteStream(options) {
        return 'write stream';
      },
    };
    return storage;
  },
};

export { GcloudAuthenticationInstance };

bucketOperations.test.ts

代码语言:javascript
复制
import pump from 'pump';
import fs from 'fs';
import { GcloudAuthenticationInstance } from './gcloudAuthenticationInstance';

jest.mock('pump', () => {
  const mPump = { on: jest.fn() };
  return jest.fn(() => mPump);
});

describe('61031410', () => {
  let originalEnv;
  beforeEach(() => {
    originalEnv = process.env;
  });
  afterEach(() => {
    process.env = originalEnv;
    jest.restoreAllMocks();
  });
  it('should upload file correctly', async () => {
    process.env.ENV_NAME_DEV = 'dev';
    process.env.GCLOUD_ENV_STR_BUCKET_NAME = 'bucket-dev';
    process.env.GCLOUD_UPLOAD_FILE_DEV_LOCAL_PATH = 'dev';
    process.env.GCLOUD_DATABASE_BUCKET_DEV = 'bucket-dev-db';
    const BucketOperations = require('./bucketOperations');
    const createReadStreamSpy = jest.spyOn(fs, 'createReadStream').mockReturnValueOnce('rs' as any);
    const mStorage: any = {
      bucket: jest.fn().mockReturnThis(),
      file: jest.fn().mockReturnThis(),
      createWriteStream: jest.fn().mockReturnValueOnce('ws'),
    };
    const infoSpy = jest.spyOn(console, 'info');
    const createGcloudAuthenticationBucketSpy = jest
      .spyOn(GcloudAuthenticationInstance, 'createGcloudAuthenticationBucket')
      .mockReturnValueOnce(mStorage);
    pump().on.mockImplementation(function(this: any, event, callback) {
      if (event === 'finish') {
        callback();
      }
      return this;
    });
    const actual = await BucketOperations.uploadEnvFiles('dev');
    expect(actual).toEqual({ status: 'Success', code: 201, error: null });
    expect(createGcloudAuthenticationBucketSpy).toBeCalledTimes(1);
    expect(pump).toBeCalledWith('rs', 'ws');
    expect(createReadStreamSpy).toBeCalledWith('dev');
    expect(mStorage.bucket).toBeCalledWith('bucket-dev');
    expect(mStorage.file).toBeCalledWith('bucket-dev-db');
    expect(mStorage.createWriteStream).toBeCalledWith({ gzip: true, public: true, resumable: true });
    expect(infoSpy.mock.calls[0]).toEqual(['after authentication']);
    expect(infoSpy.mock.calls[1]).toEqual(['Successfully uploaded the file']);
  });
  it('should handle the error if upload file failure', () => {
    // TODO: you can do this like above
  });
});

带覆盖报告的单元测试结果:

代码语言:javascript
复制
 PASS  stackoverflow/61031410/bucketOperations.test.ts (7.94s)
  61031410
    ✓ should upload file correctly (69ms)
    ✓ should handle the error if upload file failure

  console.info node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866
    after authentication

  console.info node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866
    Successfully uploaded the file

---------------------------------|---------|----------|---------|---------|-------------------
File                             | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------------------------|---------|----------|---------|---------|-------------------
All files                        |   80.56 |       50 |   54.55 |   79.41 |                   
 appUtilServiceInstance.ts       |     100 |      100 |     100 |     100 |                   
 bucketOperations.ts             |   92.31 |       50 |   83.33 |   91.67 | 40,41             
 gcloudAuthenticationInstance.ts |   28.57 |      100 |       0 |   28.57 | 3,5,8,11,14       
---------------------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        9.247s

源代码:https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/61031410

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61031410

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档