【问题标题】:How mock a third-party module in Jest/NestJS?如何在 Jest/NestJS 中模拟第三方模块?
【发布时间】:2021-07-23 19:40:59
【问题描述】:

我有以下代码,我需要模拟 connection.execute() 函数,因为它属于第三方库 snowflake-sdk 并且不属于我的单元测试:

import * as SnowflakeSDK from 'snowflake-sdk';
import { Injectable } from '@nestjs/common';

@Injectable()
export class SnowflakeClient {
  export(bucket: string, filename: string, query: string) {
    return new Promise((resolve, reject) => {
      const connection = this.getConnection();
      const command = `COPY INTO '${bucket}${filename}' FROM (${query})`;
      connection.execute({
        sqlText: command,
        complete: function (err) {
          try {
            if (err) {
              return reject(err);
            } else {
              return resolve(true);
            }
          } finally {
            connection.destroy(function (err) {
              if (err) {
                console.error('Unable to disconnect: ' + err.message);
              }
            });
          }
        },
      });
    });
  }

  getConnection() {
    const connection = SnowflakeSDK.createConnection({
      account: process.env.SNOWFLAKE_HOST!,
      username: process.env.SNOWFLAKE_USERNAME!,
      password: process.env.SNOWFLAKE_PASSWORD!,
      role: process.env.SNOWFLAKE_ROLE!,
      warehouse: process.env.SNOWFLAKE_WAREHOUSE!,
      database: process.env.SNOWFLAKE_DATABASE!,
      schema: process.env.SNOWFLAKE_SCHEMA!,
    });

    connection.connect(function (err: Error): void {
      if (err) {
        throw err;
      }
    });

    return connection;
  }
}

所以我创建了以下单元测试:

import { SnowflakeClient } from 'src/snowflake/snowflake-client';
import { Test } from '@nestjs/testing';

describe('SnowflakeClient', () => {
  let snowflakeClient: SnowflakeClient;

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      providers: [SnowflakeClient],
    }).compile();

    snowflakeClient = moduleRef.get<SnowflakeClient>(SnowflakeClient);
  });

  describe('export', () => {
    it('should export a SQL query', async () => {
      const connectionMock = jest.fn().mockImplementation(() => ({
        execute: function (bucket: string, filename: string, sql: string) {
          console.log(`${bucket}${filename}: ${sql}`);
        },
      }));

      jest.spyOn(snowflakeClient, 'getConnection').mockImplementation(connectionMock);
      const bucket = 's3://bucketName';
      const filename = '/reports/test.csv';
      const sql = 'select * from customers limit 100';
      await expect(await snowflakeClient.export(bucket, filename, sql)).resolves.not.toThrow();
    }, 10000);
  });
});

但是connectionMock.execute 没有被正确调用,因为我收到以下错误:

 FAIL  src/snowflake/snowflake-client.spec.ts (13.518 s)
  SnowflakeClient
    export
      ✕ should export a SQL query (10018 ms)

  ● SnowflakeClient › export › should export a SQL query

    thrown: "Exceeded timeout of 10000 ms for a test.
    Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

      14 |
      15 |   describe('export', () => {
    > 16 |     it('should export a SQL query', async () => {
         |     ^
      17 |       const connectionMock = jest.fn().mockImplementation(() => ({
      18 |         execute: function (bucket: string, filename: string, sql: string) {
      19 |           console.log(`${bucket}${filename}: ${sql}`);

      at src/snowflake/snowflake-client.spec.ts:16:5
      at src/snowflake/snowflake-client.spec.ts:15:3
      at Object.<anonymous> (src/snowflake/snowflake-client.spec.ts:4:1)

我想测试SnowflakeClient.export() 方法,但我需要模拟snowflake-sdk 模块,因为它不是我代码的一部分。

有人知道我做错了什么吗?

【问题讨论】:

    标签: node.js jestjs nestjs


    【解决方案1】:

    在使用sdk时,最好通过带有自定义provider的Ne​​stJs的注入系统来传递它们:

    //////////////////////////////////
    // 1. Provide the sdk in the module.
    //////////////////////////////////
    
    const SnowflakeConnectionProvider = {
      provide: 'SNOWFLAKE_CONNECTION',
      useFactory: () => {
        const connection = SnowflakeSDK.createConnection({
          account: process.env.SNOWFLAKE_HOST!,
          username: process.env.SNOWFLAKE_USERNAME!,
          password: process.env.SNOWFLAKE_PASSWORD!,
          role: process.env.SNOWFLAKE_ROLE!,
          warehouse: process.env.SNOWFLAKE_WAREHOUSE!,
          database: process.env.SNOWFLAKE_DATABASE!,
          schema: process.env.SNOWFLAKE_SCHEMA!,
        });
    
        connection.connect(function (err: Error): void {
          if (err) {
            throw err;
          }
        });
    
        return connection;
      },
      inject: [],
    }
    
    @Module({
      providers: [
        SnowflakeClient,
        SnowflakeConnectionProvider,
      ]
    })
    export class SnowflakeModule {}
    
    
    //////////////////////////////////
    // 2. Inject the connection into your `SnowflakeClient`
    //////////////////////////////////
    
    @Injectable()
    export class SnowflakeClient {
      constructor(@Inject('SNOWFLAKE_CONNECTION') private readonly connection: SnowflakeConnection) {}
    
      // ...
    }
    
    
    //////////////////////////////////
    // 3. Mock the dependency in tests
    //////////////////////////////////
    
    beforeEach(async () => {
      const moduleRef = await Test.createTestingModule({
        providers: [
          {
            provide: 'SNOWFLAKE_CONNECTION',
            useValue: { /* Mock */},
          },
          SnowflakeClient,
        ],
      }).compile();
    
      snowflakeClient = moduleRef.get<SnowflakeClient>(SnowflakeClient);
    });
    

    您将对测试有更好的控制。

    【讨论】:

      【解决方案2】:

      所以我解决了这个问题(但我不确定这是最好的方法):

        const connectionMock = jest.createMockFromModule<Connection>('snowflake-sdk');
        connectionMock.execute = jest.fn().mockImplementation(() => {
          return Promise.resolve(null);
        });
        jest.spyOn(snowflakeClient, 'getConnection').mockImplementation(() => {
          return connectionMock;
        });
        expect(snowflakeClient.export(bucket, filename, sql)).resolves.not.toThrow();
      

      让我知道你的想法:)

      【讨论】:

        猜你喜欢
        • 2018-02-19
        • 1970-01-01
        • 1970-01-01
        • 2018-11-17
        • 2019-12-14
        • 1970-01-01
        • 2014-05-31
        • 2022-08-24
        • 2020-08-28
        相关资源
        最近更新 更多