在单元测试中模拟注入的 Twilio 服务 Nest.js
Mock Injected Twilio Service in Unit Testing Nest.js
我在 nest.js 测试应用程序中覆盖 provider/setup 模块测试时遇到问题。
模块文件:smsModule.ts:
import { TwilioService } from './twilio/twilio.service';
import { DynamicModule, Module } from '@nestjs/common';
import { TwilioConfig, SMS_TWILIO_CONFIG } from './twilio.config';
import { TwilioClientCustom } from './twilio/twilio-client-custom';
@Module({
imports: [],
providers: [TwilioService],
})
export class SmsModule {
static register(options: TwilioConfig): DynamicModule {
return {
module: SmsModule,
imports: [HttpModule],
providers: [
{
provide: SMS_TWILIO_CONFIG,
useValue: options,
},
TwilioService,
TwilioClientCustom,
],
exports: [TwilioService],
};
}
}
Twilio 客户端,配置文件:
//client
import { TwilioConfig, SMS_TWILIO_CONFIG } from '../twilio.config';
import { Twilio } from 'twilio';
import { Inject, Injectable } from '@nestjs/common';
@Injectable()
export class TwilioClientCustom extends Twilio {
constructor(@Inject(SMS_TWILIO_CONFIG) twilioConfig: TwilioConfig) {
super(twilioConfig.accountSid, twilioConfig.authToken);
}
}
//config
import { IsString, IsNotEmpty, NotContains, IsOptional, IsArray } from 'class-validator';
const INFO = 'Must be ....';
export class TwilioConfig {
@IsString()
@IsNotEmpty()
@NotContains('OVERRIDE_WITH_', { message: INFO })
accountSid: string;
@IsString()
@IsNotEmpty()
authToken: string;
@IsArray()
@IsOptional()
@IsNotEmpty('OVERRIDE_WITH_', { message: INFO })
serviceSid: string;
}
export const SMS_TWILIO_CONFIG = 'smsTwilioConfig';
Twilio 服务文件:twilio.service.tst:
import { HttpService } from '@nestjs/axios';
import { TwilioConfig, SMS_TWILIO_CONFIG } from '../twilio.config';
import { SendSmsTwilioService } from './../sendsms.service';
import { Inject, Injectable } from '@nestjs/common';
import { TwilioClientCustom } from './twilio-client-custom';
@Injectable()
export class TwilioService implements SendSmsTwilioService {
constructor(
@Inject(SMS_TWILIO_CONFIG) private readonly config: TwilioConfig,
private readonly client: TwilioClientCustom,
private readonly httpService: HttpService
) {}
async sendSMS(to: string, from: string, body: string): Promise<string> {
......
return this.client.messages
.create({
to, //Recipinet's number
from, //Twilio number
body, //Messages to Recipient
})
.then((message) => message.sid)
.catch(() => {
throw new Error('TWILIO accountSid or authToken not valid');
});
}
我想测试我的服务:
测试文件:
import { Test, TestingModule } from '@nestjs/testing';
//import { TWILIO_CONFIG_SPEC } from './test.config';
import { TwilioClientCustom } from '../src/twilio/twilio-client-custom';
import { HttpService } from '@nestjs/axios';
import { TwilioConfig } from './../src/twilio.config';
import { TwilioService } from './../src/twilio/twilio.service';
import nock from 'nock';
describe('TwilioService', () => {
let service: TwilioService;
let client: TwilioClientCustom;
let httpService: HttpService;
afterEach(() => {
nock.cleanAll();
});
//const smsServiceMock = {};
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
TwilioService,
{
provide: HttpService,
useValue: {
method1: jest.fn(),
method2: jest.fn(),
method3: jest.fn(),
},
},
TwilioService,
],
imports: [
NestConfigModule.forRoot({
config: TwilioConfig,
} as Record<string, unknown>),
],
}).compile();
//getting service module from main module
httpService = moduleRef.get<HttpService>(HttpService);
client = moduleRef.get<TwilioClientCustom>(TwilioClientCustom);
service = moduleRef.get<TwilioService>(TwilioService);
});
//check service is avaible
it('Should be defined', () => {
expect(client).toBeDefined();
expect(service).toBeDefined();
expect(httpService).toBeDefined();
});
在 运行 测试后我得到以下错误:
Nest can't resolve dependencies of the TwilioService (?, TwilioClientCustom, HttpService). Please make sure that the argument smsTwilioConfig at index [0] is available in the RootTestModule context.
Potential solutions:
- If smsTwilioConfig is a provider, is it part of the current RootTestModule?
- If smsTwilioConfig is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing smsTwilioConfig */ ]
})
我该如何解决这个问题?
smsTwilioConfig
通过 SmsModule.register(opts)
.
向 Nest 的 IOC 注册
但是,您似乎正试图用 createTestingModule
直接测试 TwilioService
。这很好,但这确实意味着您需要在测试中包含提供程序的配置或导入。
我的猜测是您认为 NestConfigModule...
会那样做,但那不是在正确的级别设置配置。
我认为以下是正确的方向
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
TwilioService,
{
provide: HttpService,
useValue: {
method1: jest.fn(),
method2: jest.fn(),
method3: jest.fn(),
},
},
{
// added this
provide: SMS_TWILIO_CONFIG,
useValue: testConfig
},
],
imports: [
// removed NestConfigModule
],
}).compile();
我在 nest.js 测试应用程序中覆盖 provider/setup 模块测试时遇到问题。
模块文件:smsModule.ts:
import { TwilioService } from './twilio/twilio.service';
import { DynamicModule, Module } from '@nestjs/common';
import { TwilioConfig, SMS_TWILIO_CONFIG } from './twilio.config';
import { TwilioClientCustom } from './twilio/twilio-client-custom';
@Module({
imports: [],
providers: [TwilioService],
})
export class SmsModule {
static register(options: TwilioConfig): DynamicModule {
return {
module: SmsModule,
imports: [HttpModule],
providers: [
{
provide: SMS_TWILIO_CONFIG,
useValue: options,
},
TwilioService,
TwilioClientCustom,
],
exports: [TwilioService],
};
}
}
Twilio 客户端,配置文件:
//client
import { TwilioConfig, SMS_TWILIO_CONFIG } from '../twilio.config';
import { Twilio } from 'twilio';
import { Inject, Injectable } from '@nestjs/common';
@Injectable()
export class TwilioClientCustom extends Twilio {
constructor(@Inject(SMS_TWILIO_CONFIG) twilioConfig: TwilioConfig) {
super(twilioConfig.accountSid, twilioConfig.authToken);
}
}
//config
import { IsString, IsNotEmpty, NotContains, IsOptional, IsArray } from 'class-validator';
const INFO = 'Must be ....';
export class TwilioConfig {
@IsString()
@IsNotEmpty()
@NotContains('OVERRIDE_WITH_', { message: INFO })
accountSid: string;
@IsString()
@IsNotEmpty()
authToken: string;
@IsArray()
@IsOptional()
@IsNotEmpty('OVERRIDE_WITH_', { message: INFO })
serviceSid: string;
}
export const SMS_TWILIO_CONFIG = 'smsTwilioConfig';
Twilio 服务文件:twilio.service.tst:
import { HttpService } from '@nestjs/axios';
import { TwilioConfig, SMS_TWILIO_CONFIG } from '../twilio.config';
import { SendSmsTwilioService } from './../sendsms.service';
import { Inject, Injectable } from '@nestjs/common';
import { TwilioClientCustom } from './twilio-client-custom';
@Injectable()
export class TwilioService implements SendSmsTwilioService {
constructor(
@Inject(SMS_TWILIO_CONFIG) private readonly config: TwilioConfig,
private readonly client: TwilioClientCustom,
private readonly httpService: HttpService
) {}
async sendSMS(to: string, from: string, body: string): Promise<string> {
......
return this.client.messages
.create({
to, //Recipinet's number
from, //Twilio number
body, //Messages to Recipient
})
.then((message) => message.sid)
.catch(() => {
throw new Error('TWILIO accountSid or authToken not valid');
});
}
我想测试我的服务: 测试文件:
import { Test, TestingModule } from '@nestjs/testing';
//import { TWILIO_CONFIG_SPEC } from './test.config';
import { TwilioClientCustom } from '../src/twilio/twilio-client-custom';
import { HttpService } from '@nestjs/axios';
import { TwilioConfig } from './../src/twilio.config';
import { TwilioService } from './../src/twilio/twilio.service';
import nock from 'nock';
describe('TwilioService', () => {
let service: TwilioService;
let client: TwilioClientCustom;
let httpService: HttpService;
afterEach(() => {
nock.cleanAll();
});
//const smsServiceMock = {};
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
TwilioService,
{
provide: HttpService,
useValue: {
method1: jest.fn(),
method2: jest.fn(),
method3: jest.fn(),
},
},
TwilioService,
],
imports: [
NestConfigModule.forRoot({
config: TwilioConfig,
} as Record<string, unknown>),
],
}).compile();
//getting service module from main module
httpService = moduleRef.get<HttpService>(HttpService);
client = moduleRef.get<TwilioClientCustom>(TwilioClientCustom);
service = moduleRef.get<TwilioService>(TwilioService);
});
//check service is avaible
it('Should be defined', () => {
expect(client).toBeDefined();
expect(service).toBeDefined();
expect(httpService).toBeDefined();
});
在 运行 测试后我得到以下错误:
Nest can't resolve dependencies of the TwilioService (?, TwilioClientCustom, HttpService). Please make sure that the argument smsTwilioConfig at index [0] is available in the RootTestModule context.
Potential solutions:
- If smsTwilioConfig is a provider, is it part of the current RootTestModule?
- If smsTwilioConfig is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing smsTwilioConfig */ ]
})
我该如何解决这个问题?
smsTwilioConfig
通过 SmsModule.register(opts)
.
但是,您似乎正试图用 createTestingModule
直接测试 TwilioService
。这很好,但这确实意味着您需要在测试中包含提供程序的配置或导入。
我的猜测是您认为 NestConfigModule...
会那样做,但那不是在正确的级别设置配置。
我认为以下是正确的方向
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
TwilioService,
{
provide: HttpService,
useValue: {
method1: jest.fn(),
method2: jest.fn(),
method3: jest.fn(),
},
},
{
// added this
provide: SMS_TWILIO_CONFIG,
useValue: testConfig
},
],
imports: [
// removed NestConfigModule
],
}).compile();