I am learning NestJS and I am trying to test a service which incorporates a repository with TypeORM. It compiles fine without errors, but when I run the test it throw an error.
I know it is a test dependency problem but I can't figure it out. I am trying this test:
describe('JokesService', () => {
let service: JokesService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [],
imports: [
TypeOrmModule.forFeature([JokeEntity])
],
}).compile();
service = module.get<JokesService>(JokesService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
The module:
#Module({
providers: [JokesService],
imports: [
TypeOrmModule.forFeature([JokeEntity])
],
controllers: [JokesController],
})
export class JokesModule {}
The service:
#Injectable()
export class JokesService {
constructor(
#InjectRepository(JokeEntity)
private readonly jokeRepository: MongoRepository<JokeEntity>
) {}
}
The main module:
#Module({
imports: [
TypeOrmModule.forRoot({
type: 'mongodb',
url: dbUri,
entities: [__dirname + '/**/*.entity{.ts,.js}'],
ssl: true,
useUnifiedTopology: true,
useNewUrlParser: true,
}),
JokesModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
And the error:
Nest cant resolve dependencies of the JokeEntityRepository (?).
Please make sure that the argument Connection at index [0] is available in the TypeOrmModule context.
Potential solutions:
- If Connection is a provider, is it part of the current TypeOrmModule?
- If Connection is exported from a separate #Module, is that module imported within TypeOrmModule?
#Module({
imports: [ /* the Module containing Connection */ ]
})
Can someone tell me what I am missing in the test dependencies? Thank You.
Try to import your JokesModule inside your app.module.
#Module({
imports: [
TypeOrmModule.forRoot({
type: 'mongodb',
url: dbUri,
entities: [__dirname + '/**/*.entity{.ts,.js}'],
ssl: true,
useUnifiedTopology: true,
useNewUrlParser: true,
}),
JokesModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Related
I need to use the ConfigService to get the environment variables, but when I use "forRootAsync" of the TypeOrmModule nestjs is unable to resolve the DataService dependency
This is my module
#Module({
imports: [
ConfigModule.forRoot({ validate: validateConfig(AssetEnvironmentVariables), isGlobal: true }),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService): TypeOrmModuleOptions => ({
type: "postgres",
host: configService.get("PG_HOST"),
port: configService.get("PG_PORT"),
database: configService.get("PG_DB_NAME"),
username: configService.get("PG_USER"),
password: configService.get("PG_PASSWORD"),
entities: [AssetEntity],
migrations: ["./migrations/*"],
}),
});
],
providers: [
{ provide: AssetRepository, useClass: AssetPostgresRepository },
],
})
export class AssetModule {}
This is the implementation to AssetRepository
#Injectable()
export class AssetPostgresRepository extends AssetRepository {
private typeOrmRepository: Repository<AssetEntity>;
constructor(dataSource: DataSource) {
super();
this.typeOrmRepository = dataSource.getRepository(AssetEntity);
}
async save(asset: Asset): Promise<void> {
try {
await this.typeOrmRepository.save(asset.toPrimitives());
} catch (e) {
throw new SavingRepositoryException(e);
}
}
}
This is the error that it throw me
ERROR [ExceptionHandler] Nest can't resolve dependencies of the AssetPostgresRepository (?). Please make sure that the argument DataSource at index [0] is available in the AssetModule context.
Potential solutions:
- Is AssetModule a valid NestJS module?
- If DataSource is a provider, is it part of the current AssetModule?
- If DataSource is exported from a separate #Module, is that module imported within AssetModule?
#Module({
imports: [ /* the Module containing DataSource */ ]
})
Okay so I'm using TYPEORM_ prefixed environment variables to register TypeOrmModule into my app. Then I'm loading entities using .forFeature() like so:
#Module({
imports: [TypeOrmModule.forFeature([Foo])],
controllers: [FooController],
providers: [FooService],
exports: [TypeOrmModule],
})
export class FooModule {}
No matter whether I use forRoot() or forRoot({ autoLoadEntities: true }) in AppModule, I get RepositoryNotFoundError: No repository for "Foo" was found. Looks like this entity is not registered in current "default" connection? error while working with the dev server. Any idea what's going on? My service looks like:
#Injectable()
export class FooService {
constructor(#InjectRepository(Foo) private readonly fooRepository: Repository<Foo>) {}
}
I tried the followings as single and/or combined, still didn't solve the issue.
Put a name in #Entity() decorator.
#Entity('foo')
export class Foo {}
Try forRootAsync().
#Module({
imports: [
TypeOrmModule.forRootAsync({
useFactory: async () =>
Object.assign(await getConnectionOptions(), { autoLoadEntities: true }),
}),
FooModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Pass 'default' to forFeature().
#Module({
imports: [TypeOrmModule.forFeature([Foo], 'default')],
controllers: [FooController],
providers: [FooService],
exports: [TypeOrmModule],
})
export class FooModule {}
Try using entities key in forRoot().
#Module({
imports: [
TypeOrmModule.forRoot({ entities: [Foo] }),
FooModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
try to define connectionName inside getConnectionOptions
example:
#Module({
imports: [
TypeOrmModule.forRootAsync({
useFactory: async () =>
Object.assign(await getConnectionOptions('default'), { autoLoadEntities: true }),
}),
FooModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
I followed the the Nest documentation to create the config but it's not working
app.module.ts
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRoot(config),
AuthModule,
UsersModule,
MailModule,
CloudinaryModule,
],
controllers: [AppController],
providers: [AppService],
})
.env file is on the src folder
mail.module.ts
#Module({
imports: [
MailerModule.forRoot({
transport: {
service: 'Gmail',
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASS,
},
},
}),
],
But when I run the app its undefined my key value pairs are also there.
The problem is ConfigModule's env variables are only available at run time but not on the nestjs initial state.
To allow getting the .env after nestjs initialised, you can use async config to in MailerModule.
mail.config.ts
export class MailerConfig implements MailerOptionsFactory {
createMailerOptions(): MailerOptions | Promise<MailerOptions> {
console.log(process.env.MAIL_USER); // should have value
return {
transport: {
service: 'Gmail',
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASS,
},
},
};
}
}
mail.module.ts
console.log(process.env.MAIL_USER); // undefined
#Module({
imports: [
MailerModule.forRootAsync({
useClass: MailerConfig,
}),
],
})
export class MailModule {}
you can use useFactory as well without the need of class, here I want to console.log the .env for you to check with so i used config class.
I am following doc to start to use queue. I installed #nestjs/bull, bull, #types/bull dependencies. And here is my app.module.ts:
#Module({
imports: [
ConfigModule.forRoot({
load: [configuration],
}),
BullModule.registerQueue({
name: 'create_checkin',
redis: {
host: 'localhost',
port: 6379,
},
}),
EventModule,
],
})
export class AppModule {}
I imported BullModule in root module. And here is my event.service.ts:
#Injectable()
export class EventService {
constructor(
#InjectQueue('create_checkin') private readonly createCheckinQueue: Queue,
) {
}
}
And when I start server, I got the following error message:
Nest can't resolve dependencies of the EventService
Please make sure that the argument BullQueue_create_checkin at index [0] is available in the EventModule context.
I don't know which step I did wrong. Someone can help me?
Had a similar problem, in my case it was enough to add the BullModule to the exports array in order to successfully run the whole project. Like this:
#Module({
imports: [
BullModule.registerQueue({ ... }),
...
],
...
exports: [
BullModule, // <— this is important!
...
]
})
Then in my service I've been able to inject the queue:
#InjectQueue('print') private queue: Queue<PrintJob>
You have to import bull module inside the module you are trying to setup queue. You can also refer https://medium.com/#shikhar01.cse14/bull-queues-in-nestjs-306c51cb0ec2
Make sure you are placing EventService under providers array in EventModule.
#Module({
providers: [EventService],
controllers :[],
imports: [YOUR_MODULES],
exports: [EventService]
})
export class EventModule {}
Try importing BullModule straight in Event Module - I had the same problem and doing it this way make it work.
#Module({
imports: [
BullModule.registerQueueAsync({
name: 'csv',
useFactory: async (config: ConfigService) => ({
redis: config.get('redis'),
}),
inject: [ConfigService],
}),
],
providers: [
CsvService
],
exports: [CsvService],
})
export class CsvModule {}
I know that it's async method, but maybe you should try.
You can do like below
#Module({
imports: [
ConfigModule.forRoot({
load: [configuration],
}),
EventModule,
],
})
export class AppModule {}
#Module({
imports: [
BullModule.registerQueue({
name: 'create_checkin',
redis: {
host: 'localhost',
port: 6379,
},
}),
],
})
export class EventModule {}
I'm having a problem with overriding a provider in a test context. I know I'm missing something blindingly obvious, but I've tried loads of permutations and none have worked, so I'm putting this out to the hive mind.
I have the following structure:
shared.module.ts
#Module({
providers: [
DatabaseProvider,
ConfigService,
CacheService
],
exports: [
DatabaseProvider,
ConfigService
CacheService
],
imports: [HttpModule],
})
where DatabaseProvider is defined as:
const DatabaseProvider = {
provide: "MongoConnection",
inject: [ConfigService],
useFactory: async (config: ConfigService): Promise < typeof mongoose | Object > => {
return await mongoose.connect(config.getString("MONGO_URI"), { useNewUrlParser: true });
}
};
my.module.ts
#Module({
imports: [SharedModule, HttpModule],
exports: [MyService],
controllers: [MyController],
providers: [MyService],
})
app.module.ts
#Module({
imports: [
SharedModule,
MyModule
],
providers: [
...
],
controllers: [
...
]
})
my.service.ts
export class MyService {
constructor(
#Inject("MongoConnection") private readonly dbConnection: Connection,
private _cache: CacheService
) { }
...
}
my.spec.ts
let cacheService = {...};
this.db = {...};
const module = await Test.createTestingModule({
imports: [SharedModule, MyModule, HttpModule]
})
.overrideProvider(CacheService)
.useValue(cacheService)
.overrideProvider("MongoConnection")
.useValue(this.db)
.compile();
My issue is that the CacheService appears to be being overridden (it's using redis-mock defined in cacheService, instead of the default redis implementation in CacheService), but the MongoConnection is not - it is still calling the original DatabaseProvider from shared.module.ts.
Can someone point out what I'm missing?