如何为@Injectable()mongodb服务编写Nestjs单元测试

如何为@Injectable()mongodb服务编写Nestjs单元测试,mongodb,unit-testing,jestjs,nestjs,Mongodb,Unit Testing,Jestjs,Nestjs,有人能给我引路吗。我正在学习Nestjs并做一个小项目,但我无法让依赖于database.module的控制器和服务的单元测试工作。如何在product.service.ts中模拟database.module?我们将非常感谢您的帮助 数据库.module.ts try { const client = await MongoClient.connect(process.env.MONGODB, { useNewUrlParser: true, useUnifiedTopology:

有人能给我引路吗。我正在学习Nestjs并做一个小项目,但我无法让依赖于database.module的控制器和服务的单元测试工作。如何在product.service.ts中模拟database.module?我们将非常感谢您的帮助

数据库.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 {}
@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 (){}

}

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());

}
产品.服务.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 {}
@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 (){}

}

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());

}
产品.服务.规范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 {}
@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 (){}

}

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());

}
description('ProductService',()=>{
让服务:产品服务;
beforeach(异步()=>{
常量模块:TestingModule=等待测试。createTestingModule({
进口:[],
供应商:[
配置服务,
数据库模块,
AppConfigService,
产品服务,
{
提供:数据库连接,
useFactory:()=>{}
}
],
}).compile();
服务=模块。获取(ProductService);
});
毕竟(()=>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());

}
description('ProductController',()=>{
let-app:测试模块;
让ProductController:ProductController;
让产品服务:产品服务;
常数响应={
发送:(body?:any)=>{},
状态:(代码:编号)=>响应,
json:(body?:any)=>响应
}
beforeach(异步()=>{
app=wait Test.createTestingModule({
进口:[
ConfigModule.forRoot({
加载:[appConfig],
isGlobal:没错,
可扩展变量:true
}),
产品模块,
],
供应商:[
AppConfigService,
产品服务,
],
控制器:[ProductController]
}).compile();
productController=app.get(productController);
productService=app.get(productService);
});
毕竟(()=>jest.restoreAllMocks());
}

任何没有在单元测试中直接测试的东西理论上都应该被模拟。在这种情况下,您有两个依赖项,
AppConfigService
adn
DATABASE\u CONNECTION
。您的单元测试应该提供模拟对象,这些对象看起来像注入的依赖项,但是已经定义并易于修改行为。在这种情况下凯斯,像这样的东西可能就是你要找的

beforeach(异步()=>{
const modRef=wait Test.createTestingModule({
供应商:[
产品服务,
{
提供:AppConfigService,
使用价值:{
appConfigObject:mockConfigObject
}
},
{
提供:“数据库连接”,
使用价值:{
:jest.fn()
}
]
}).compile();
//假设这些是在顶层中定义的,请描述
prodService=modRef.get(ProductionService);
conn=modRef.get('DATABASE_CONNECTION');
config=modRef.get(AppConfigService);
});
在控制器测试中,您不应该担心模拟除
ProdctService
之外的任何东西

如果你需要更多的帮助

编辑日期:2020年4月9日 在处理Mongo之类的东西时,模拟链式方法是一个主要的难点。有几种方法可以做到这一点,但最简单的方法可能是创建一个类似Mongo的模拟对象

const mockModel={
find:jest.fn().mockReturnThis(),
更新:jest.fn().mockReturnThis(),
排序规则:jest.fn().mockReturnThis(),
等
}
在链中的最后一个调用中,让它返回预期的结果,以便您的服务可以继续运行其余的代码

const value=model.find().collation().skip().limit().exec()
您需要设置
exec()
方法来返回您希望它返回的值,可能需要使用

jest.spyOn(mockModel,'exec').mockResolvedValueOnce(queryReturn);

任何没有在单元测试中直接测试的东西理论上都应该被模拟。在这种情况下,您有两个依赖项,
AppConfigService
adn
DATABASE\u CONNECTION
。您的单元测试应该提供模拟对象,这些对象看起来像注入的依赖项,但是已经定义并易于修改行为。在这种情况下凯斯,像这样的东西可能就是你要找的

beforeach(异步()=>{
const modRef=wait Test.createTestingModule({
供应商:[
产品服务,
{
提供:AppConfigService,
使用价值:{
appConfigObject:mockConfigObject
}
},
{
提供:“数据库连接”,
使用价值:{
:jest.fn()
}
]
}).compile();
//假设这些是在顶层中定义的,请描述
prodService=modRef.get(ProductionService);
conn=modRef.get('DATABASE_CONNECTION');
config=modRef.get(AppConfigService);
});
在控制器测试中,您不应该担心模拟除
ProdctService
之外的任何东西

如果你需要更多的帮助

编辑日期:2020年4月9日 在处理Mongo之类的东西时,模拟链式方法是一个主要的难点。有几种方法可以做到这一点,但最简单的方法可能是创建一个类似Mongo的模拟对象

const mockModel={
find:jest.fn().mockReturnThis(),
更新:jest.fn().mockReturnThis(),
排序规则:jest.fn().mockReturnThis(),
等
}
在链中的最后一个调用中,让它返回预期的结果,以便您的服务可以继续运行其余的代码

const value=model.find().collation().skip().limit().exec()
您需要设置
exec()
方法来返回您希望它返回的值,可能需要使用

jest.spyOn(mockModel,'exec').mockResolvedValueOnce(queryReturn);

您当前的解决方案出了什么问题?您当前的解决方案出了什么问题?我确实遇到了您的示例,并了解了其设置和功能