I'm trying to execute useMocker on the following piece of code:
const module = await Test.createTestingModule({
providers: [
RatingService,
UserService,
RentService,
{ provide: getRepositoryToken(RatingORM), useValue: repositoryMockFactory(ratingStub()) },
],
}).useMocker((token) => {
switch(token) {
case UserService: return UserService;
case RentService: return RentService;
}
}).compile();
but I keep getting the error "Property 'useMocker' does not exist on type 'TestingModuleBuilder'"
Anyone knows why is that?
make sure you're using the right version of #nestjs/testing. Upgrade it to the latest or something
Related
I am trying to inject dependencies in mongoose module for root async. I want to kind of simulate a cascade delete using mongoose hooks.
I have this module, which imports mongoose module, and it imports other modules.
#Module({
imports: [
MongooseModule.forFeatureAsync([
{
imports: [NotificationModule, UserModule, MuseumModule, ImageModule, CommentModule],
name: Shirt.name,
useFactory: (
NotificationService: NotificationService,
UserService: UserService,
MuseumService: MuseumService,
ImageService: ImageService,
ConfigService: ConfigService,
CommentService: CommentService,
) => {
const schema = ShirtSchema;
schema.post('findOneAndDelete', async function (document: ShirtDocument) {
/* Delete notifications */
if (document) {
await NotificationService.deleteManyFromArray(document._id);
/* Remove shirt from museum */
const shirtUser = await UserService.getById(document.shirtUser.userId);
await MuseumService.removeShirtByMuseumId(shirtUser.museums[0], document._id);
/* Delete images from bucket */
if (document.images && document.images.length > 0) {
document.images.forEach(async (image) => {
if (image.thumbnail) {
await ImageService.deleteImageFromBucketS3({
bucket: ConfigService.get('AWS_THUMBNAIL_BUCKET'),
key: getImageUUID(image.thumbnail),
});
}
await ImageService.deleteImageFromBucketS3({
bucket: ConfigService.get('AWS_BUCKET'),
key: getImageUUID(image.cloudImage),
});
});
}
/* Remove shirt comments */
if (document.comments && document.comments.length > 0) {
await CommentService.deleteManyComments(document.comments.map((c) => c._id));
}
}
});
return schema;
},
inject: [NotificationService, UserService, MuseumService, ImageService, ConfigService, CommentService],
},
]),
UserModule,
TeamModule,
BrandModule,
CountryModule,
MuseumModule,
ImageModule,
],
controllers: [ShirtController],
providers: [ShirtService, ShirtRepository],
exports: [ShirtService],
})
export class ShirtModule {}
I also need to do the same in another module, but when I import the
ShirtModule
the compilation fails with the following error:
Error: Nest cannot create the module instance. Often, this is because
of a circular dependency between modules. Use forwardRef() to avoid
it. Scope [AppModule -> UserModule -> MongooseModule -> MuseumModule
-> MongooseModule -> ShirtModule -> MongooseModule]
#Module({
imports: [
MongooseModule.forFeatureAsync([
{
name: 'Museum',
imports: [ShirtModule],
useFactory: () => {
const schema = MuseumSchema;
schema.post('findOneAndDelete', async function (document: MuseumDocument) {});
return schema;
},
},
]),
],
controllers: [MuseumController],
providers: [MuseumService, MuseumRepository],
exports: [MuseumService],
})
export class MuseumModule {}
I tried using
forwardRef(() => )
In both modules, but still the same. I can not understand where is the circular dependency and how to solve it.
Please could you help me?. Also, is this is a good approach to use mongoose hooks using nest?
Thanks
Try to use forwardRef(() => MongooseModule.forFeatureAsync(xxxx)). This is work in my case.
I have a CategoryService that is a provider of CategoriesModule:
#Module({
imports: [
MongooseModule.forFeatureAsync([
{
name: Category.name,
imports: [EventEmitterModule],
inject: [EventEmitter2],
useFactory: (eventEmitter: EventEmitter2) => {
const schema = CategorySchema
schema.post('findOneAndDelete', (category: CategoryDocument) => eventEmitter.emit(CollectionEvents.CategoriesDeleted, [category]))
return schema
}
}
])
],
providers: [CategoriesService]
})
export class CategoriesModule {
}
My CategoriesService is:
#Injectable()
export class CategoriesService {
constructor(#InjectModel(Category.name) private categoryModel: Model<CategoryDocument>) {
}
...
}
Then I have a jest test file of that service categories.service.spec.ts:
describe('CategoriesService', () => {
let service: CategoriesService
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [CategoriesService]
}).compile()
service = module.get<CategoriesService>(CategoriesService)
})
it('should be defined', () => {
expect(service).toBeDefined()
})
})
But when i run the test (using nestJs built in script test) if failes with this error:
Nest can't resolve dependencies of the CategoriesService (?). Please make sure that the argument CategoryModel at index [0] is available in the RootTestModule context.
Potential solutions:
- If CategoryModel is a provider, is it part of the current RootTestModule?
- If CategoryModel is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing CategoryModel */ ]
})
But I don't get it, when running the server using npm start it all runs ok,
and here it complains about CategoryModel, why?
Category model is used in CategoryService and hence is a dependency for CategoryService. You will need to add the model to your spec file so that the dependency is resolved.
If you are using mongoose look at this answer it will help you.
I am using Angular and Electron, and I have a Preferences object I would like to initialize (Preferences.init()) which needs to be executed before any other code is executed. Does Angular or Electron have a specific location where such initialization code should be executed?
At the moment I put it into the constructor of AppComponent but since the function is asynchronous, I have a race condition and occasionally the data is not properly initialized when needed. Any help or suggestion is highly appreciated! Thanks!
You can use the APP_INITIALIZER dependency injection token to do a such thing:
Assuming you created a provider
#Injectable()
export class ThingProvider {
private thing: Thing = null;
constructor(private http: Http) {
}
public getThing(): Thing {
return this.thing;
}
load() {
return new Promise((resolve, reject) => {
this.http
.get('https://my-api.com/thing')
.map(res => res.json())
.subscribe(response => {
this.thing = response['value'];
resolve(true);
})
})
}
}
Then, in your app.module.ts file:
export function thingsProviderFactory(provider: ThingProvider) {
return () => provider.load();
}
And finally, in the same file:
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpModule
],
providers: [
ThingProvider,
{ provide: APP_INITIALIZER, useFactory: thingsProviderFactory, deps: [ThingProvider], multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
I'm trying to unit test this controller and mock away the services/repositories that it needs.
#Controller('auth')
export class AuthController {
constructor(
private readonly authService: AuthService,
private readonly usersService: UsersService,
) {}
#Post('register')
public async registerAsync(#Body() createUserModel: CreateUserModel) {
const result = await this.authenticationService.registerUserAsync(createUserModel);
// more code here
}
#Post('login')
public async loginAsync(#Body() login: LoginModel): Promise<{ accessToken: string }> {
const user = await this.usersService.getUserByUsernameAsync(login.username);
// more code here
}
}
Here is my unit test file:
describe('AuthController', () => {
let authController: AuthController;
let authService: AuthService;
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [JwtModule],
controllers: [AuthController],
providers: [
AuthService,
UsersService,
{
provide: getRepositoryToken(User),
useClass: Repository,
},
],
}).compile();
authController = moduleRef.get<AuthenticationController>(AuthenticationController);
authService = moduleRef.get<AuthenticationService>(AuthenticationService);
});
describe('registerAsync', () => {
it('Returns registration status when user registration succeeds', async () => {
let createUserModel: CreateUserModel = {...}
let registrationStatus: RegistrationStatus = {
success: true,
message: 'User registered successfully',
};
jest.spyOn(authService, 'registerUserAsync').mockImplementation(() =>
Promise.resolve(registrationStatus),
);
expect(await authController.registerAsync(createUserModel)).toEqual(registrationStatus);
});
});
});
But when running this, I get the following error(s):
● AuthController › registerAsync › Returns registration status when user registration succeeds
Nest can't resolve dependencies of the JwtService (?). Please make sure that the argument JWT_MODULE_OPTIONS at index [0] is available in the JwtModule context.
Potential solutions:
- If JWT_MODULE_OPTIONS is a provider, is it part of the current JwtModule?
- If JWT_MODULE_OPTIONS is exported from a separate #Module, is that module imported within JwtModule?
#Module({
imports: [ /* the Module containing JWT_MODULE_OPTIONS */ ]
})
at Injector.lookupComponentInParentModules (../node_modules/#nestjs/core/injector/injector.js:191:19)
at Injector.resolveComponentInstance (../node_modules/#nestjs/core/injector/injector.js:147:33)
at resolveParam (../node_modules/#nestjs/core/injector/injector.js:101:38)
at async Promise.all (index 0)
at Injector.resolveConstructorParams (../node_modules/#nestjs/core/injector/injector.js:116:27)
at Injector.loadInstance (../node_modules/#nestjs/core/injector/injector.js:80:9)
at Injector.loadProvider (../node_modules/#nestjs/core/injector/injector.js:37:9)
at Injector.lookupComponentInImports (../node_modules/#nestjs/core/injector/injector.js:223:17)
at Injector.lookupComponentInParentModules (../node_modules/#nestjs/core/injector/injector.js:189:33)
● AuthController › registerAsync › Returns registration status when user registration succeeds
Cannot spyOn on a primitive value; undefined given
48 | };
49 |
> 50 | jest.spyOn(authService, 'registerUserAsync').mockImplementation(() =>
| ^
51 | Promise.resolve(registrationStatus),
52 | );
53 |
at ModuleMockerClass.spyOn (../node_modules/jest-mock/build/index.js:780:13)
at Object.<anonymous> (Authentication/authentication.controller.spec.ts:50:18)
I'm not quite sure how to proceed so I'd like some help.
There's a few things I'm noticing here:
if you're testing the controller, you shouldn't need to mock more than one level deep pf services
you should almost never have a use case where you need an imports array in a unit test.
What you can do for your test case instead is something similar to the following:
beforeEach(async () => {
const modRef = await Test.createTestingModule({
controllers: [AuthController],
providers: [
{
provide: AuthService,
useValue: {
registerUserAsync: jest.fn(),
}
},
{
provide: UserService,
useValue: {
getUserByUsernameAsync: jest.fn(),
}
}
]
}).compile();
});
Now you can get the auth service and user service using modRef.get() and save them to a variable to add mocks to them later. You can check out this testing repository which has a lot of other examples.
Since you are registering AuthService in the dependency injection container and just spying on registerUserAsync, it requires JWTService to be registered as well.
You need to register dependencies that are injected in AuthService:
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [JwtModule],
controllers: [AuthController],
providers: [
AuthService,
UsersService,
JWTService, // <--here
{
provide: getRepositoryToken(User),
useClass: Repository,
},
],
}).compile();
or register a fully mocked AuthService that doesn't need any other dependency:
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [JwtModule],
controllers: [AuthController],
providers: [
{
provide: AuthService,
useValue: {
registerUserAsync: jest.fn(), // <--here
}
},
{
provide: getRepositoryToken(User),
useClass: Repository,
},
],
}).compile();
If you're building out a full integration test suite for NestJS then it will be easy to hit this error if you import a module that imports the AuthService. That will inevitably require the JwtService which will error out with: Nest can't resolve dependencies of the JwtService (?). Please make sure that the argument JWT_MODULE_OPTIONS at index [0] is available in the RootTestModule context.
Here's how I resolved this. I added:
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
secret: configService.get('JWT_SECRET'),
signOptions: { expiresIn: '1d' }
})
}),
To my imports: [] function inside my await Test.createTestingModule({ call
The final important thing to do was to not import JwtService directly. Instead, initialize JwtModule with the above code which by extension itself should internally initialize JwtService correctly.
I am new to nest js and typescript also. Thanks in advance.
I am getting this error continuously.
Nest can't resolve dependencies of the VendorsService (?). Please verify whether [0] argument is available in thecurrent context.
Here is the code
App module
#Module({
imports: [ UsersModule, VendorsModule],
})
export class ApplicationModule {}
controller
#Controller()
export class VendorsController {
constructor(private readonly vendorService: VendorsService){}
#Post()
async create(#Body() createVendorDTO: CreateVendorDTO){
this.vendorService.create(createVendorDTO);
}
#Get()
async findAll(): Promise<Vendor[]>{
return this.vendorService.findAll();
}
}
Service
#Injectable()
export class VendorsService {
constructor(#Inject('VendorModelToken') private readonly vendorModel: Model<Vendor>) {}
async create(createVendorDTO: CreateVendorDTO): Promise<Vendor>{
const createdVendor = new this.vendorModel(createVendorDTO);
return await createdVendor.save();
}
async findAll(): Promise<Vendor[]>{
return await this.vendorModel.find().exec();
}
}
provider
export const usersProviders = [
{
provide: 'VendorModelToken',
useFactory: (connection: Connection) => connection.model('Vendor', VendorSchema),
inject: ['DbConnectionToken'],
},
];
Module
#Module({
providers: [VendorsService],
controllers: [VendorsController],
})
export class VendorsModule {}
VendorsModule sould declare your provider (usersProviders) in its providers, otherwise Nestjs will never be able to inject it into your service.
Unless you wanted to declare it with UsersModule (I guess you did); in that case, UsersModule also needs it in its exports so it's made visible to other modules importing UsersModule.
It's either VendorsModule: usersProviders in providers,
Or UsersModule: usersProviders in both providers and exports