有人可以指导我吗?我正在学习Nestjs并正在做一个小项目,但我无法使单元测试适用于依赖于database.module的控制器和服务。我该如何模拟product.service.ts中的database.module?任何帮助将不胜感激。
database.module.ts
try {
const client = await MongoClient.connect(process.env.MONGODB, { useNewUrlParser: true, useUnifiedTopology: true });
return client.db('pokemonq')
} catch (e) {
console.log(e);
throw e;
}
};
@Module({
imports: [],
providers: [
{
provide: 'DATABASE_CONNECTION',
useFactory: setupDbConnection
},
],
exports: ['DATABASE_CONNECTION'],
})
export class DatabaseModule {}
product.service.ts
@Injectable()
export class ProductService {
protected readonly appConfigObj: EnvConfig;
constructor(
private readonly appConfigService: AppConfigService,
@Inject('DATABASE_CONNECTION') => **How to mock this injection?**
private db: Db,
) {
this.appConfigObj = this.appConfigService.appConfigObject;
}
async searchBy (){}
async findBy (){}
}
product.service.spec.ts
describe('ProductService', () => {
let service: ProductService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [],
providers: [
ConfigService,
DatabaseModule,
AppConfigService,
ProductService,
{
provide: DATABASE_CONNECTION,
useFactory: () => {}
}
],
}).compile();
service = module.get< ProductService >(ProductService);
});
afterAll(() => jest.restoreAllMocks());
}
product.controller.spec.ts
describe('ProductController', () => {
let app: TestingModule;
let ProductController: ProductController;
let ProductService: ProductService;
const response = {
send: (body?: any) => {},
status: (code: number) => response,
json: (body?: any) => response
}
beforeEach(async () => {
app = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
load: [appConfig],
isGlobal: true,
expandVariables: true
}),
ProductModule,
],
providers: [
AppConfigService,
ProductService,
],
controllers: [ProductController]
}).compile();
productController = app.get< ProductController >(ProductController);
productService = app.get< ProductService >(ProductService);
});
afterAll(() => jest.restoreAllMocks());
}
答案 0 :(得分:2)
任何未经单元测试直接测试的理论上都应该被嘲笑。在这种情况下,您有两个依赖项,AppConfigService
和DATABASE_CONNECTION
。您在单元测试中应该提供模拟对象,它们看起来像注入的依赖项,但具有定义且易于修改的行为。在这种情况下,您可能正在寻找类似的东西
beforeEach(async () => {
const modRef = await Test.createTestingModule({
providers: [
ProductService,
{
provide: AppConfigService,
useValue: {
appConfigObject: mockConfigObject
}
},
{
provide: 'DATABASE_CONNECTION',
useValue: {
<databaseMethod>: jest.fn()
}
]
}).compile();
// assuming these are defined in the top level describe
prodService = modRef.get(ProductionService);
conn = modRef.get('DATABASE_CONNECTION');
config = modRef.get(AppConfigService);
});
在控制器测试中,您不必担心ProdctService
以外的其他任何事物。
如果您需要更多帮助,there's a large repository of examples here
模拟链接方法是处理Mongo之类的主要难题。有几种方法可以解决,但最简单的方法可能是创建一个像
这样的模拟对象const mockModel = {
find: jest.fn().mockReturnThis(),
update: jest.fn().mockReturnThis(),
collation: jest.fn().mockReturnThis(),
...etc
}
并在链中的最后调用中,使其返回预期结果,以便您的服务可以继续运行其余代码。这意味着如果您有类似的电话
const value = model.find().collation().skip().limit().exec()
您可能需要设置exec()
方法以返回期望的值,可能使用类似的方法
jest.spyOn(mockModel, 'exec').mockResolvedValueOnce(queryReturn);
答案 1 :(得分:0)
我也在探索将原生 Mongodb 与 NestJS 结合使用。下面是我对 db 中 cron 作业服务更新值的工作测试。
src/cron/cron.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { Db } from 'mongodb';
import { Order } from 'src/interfaces/order.interface';
@Injectable()
export class CronService {
constructor(
@Inject('DATABASE_CONNECTION')
private db: Db,
) {}
@Cron(CronExpression.EVERY_30_SECONDS)
async confirmOrderEveryMinute() {
console.log('Every 30 seconds');
await this.db
.collection<Order>('orders')
.updateMany(
{
status: 'confirmed',
updatedAt: {
$lte: new Date(new Date().getTime() - 30 * 1000),
},
},
{ $set: { status: 'delivered' } },
)
.then((res) => console.log('Orders delivered...', res.modifiedCount));
}
}
src/cron/cron.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { Db } from 'mongodb';
import { CronService } from './cron.service';
describe('CronService', () => {
let service: CronService;
let connection: Db;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
CronService,
{
provide: 'DATABASE_CONNECTION',
useFactory: () => ({
db: Db,
collection: jest.fn().mockReturnThis(),
updateMany: jest.fn().mockResolvedValue({ modifiedCount: 1 }),
}),
},
],
}).compile();
service = module.get<CronService>(CronService);
connection = module.get('DATABASE_CONNECTION');
});
it('should be defined', async () => {
expect(service).toBeDefined();
});
it('should confirmOrderEveryMinute', async () => {
await service.confirmOrderEveryMinute();
expect(connection.collection('orders').updateMany).toHaveBeenCalled();
});
});