#node.js #jestjs #nestjs
Вопрос:
У меня есть следующий код, и мне нужно поиздеваться над 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
модулем, поскольку он не является частью моего кода.
Кто — нибудь знает, что я делаю не так?
Ответ №1:
Так что я исправил это, сделав это (но я не уверен, что это лучший способ).:
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();
Дайте мне знать ваши мысли 🙂
Ответ №2:
При использовании sdk лучше передавать их через систему внедрения NestJS с помощью пользовательского поставщика:
//////////////////////////////////
// 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);
});
У вас будет гораздо лучший контроль над тестами.