I have a question regarding testing as described on https://docs.nestjs.com/advanced/unit-testing:
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [CatsController],
components: [CatsService],
}).compile();
catsService = module.get<CatsService>(CatsService);
catsController = module.get<CatsController>(CatsController);
});
The CatService is quite simple. How about this?
constructor(@Inject(CAT_MODEL_TOKEN) private readonly catModel: Model<Cat>) {}
How are we supposed to provide the catModel to CatsService in beforeEach? Without providing any parameters I get an error:
Nest can't resolve dependencies of the CatsService. Please verify whether all of them are available in the current context.
Hi !
If you can inject something in your service, that supposed than you have a provider which is in the components list of your module and then it's need to appear in module configuration in your test.
something like this :
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [CatsController],
components: [
CatsService,
{
provide: 'CAT_MODEL_TOKEN',
useValue: Cat
}
],
}).compile();
catsService = module.get<CatsService>(CatsService);
catsController = module.get<CatsController>(CatsController);
});
and you need to inject it in your service like this :
constructor(@Inject(CAT_MODEL_TOKEN) private readonly catModel: typeof Model) {}
instedOf
constructor(@Inject(CAT_MODEL_TOKEN) private readonly catModel: Model<Cat>) {}
@adrien2p thanks, however I still get the same error.
Could you past your service and your test (the top of it)
@adrien2p
Service:
@Component()
export class MessageService {
constructor(@Inject(MESSAGE_MODEL_TOKEN) private readonly messageModel: typeof Model) {}
// ...
}
Test:
describe('MessageController', () => {
let messageController: MessageController;
let messageService: MessageService;
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [MessageController],
components: [MessageService, {
provide: MESSAGE_MODEL_TOKEN,
useValue: Message
}],
}).compile();
messageService = module.get<MessageService>(MessageService);
messageController = module.get<MessageController>(MessageController);
});
// ...
});
Module:
@Module({
modules: [DatabaseModule],
controllers: [MessageController],
components: [
MessageService,
...messageProviders
],
})
export class MessageModule {}
Maybe a weard question but your MESSAGE_MODEL_TOKEN is between quotes ?
Hi @ArtworkAD,
Hmm.. The code looks fine, we need more context here. 馃檨 Is MESSAGE_MODEL_TOKEN the only required dependency injected by the MessageService?
@kamilmysliwiec yes it is
originally the message service looks like
@Component()
export class MessageService {
constructor(@Inject(MESSAGE_MODEL_TOKEN) private readonly messageModel: Model<Message>) {}
// ...
}
And using this service in tests is not working:
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [MessageController],
components: [MessageService],
}).compile();
messageService = module.get<MessageService>(MessageService);
messageController = module.get<MessageController>(MessageController);
});
Nest can't resolve dependencies of the MessageService. Please verify whether all of them are available in the current context.
at Injector.<anonymous> (node_modules/@nestjs/core/injector/injector.js:156:23)
at Generator.next (<anonymous>)
at fulfilled (node_modules/@nestjs/core/injector/injector.js:4:58)
at process._tickCallback (internal/process/next_tick.js:109:7)
@ArtworkAD because there's no MESSAGE_MODEL_TOKEN provider. Did you try @adrien2p's solution?
Hi @ArtworkAD,
Any updates here 馃檪 ?
@kamilmysliwiec @adrien2p trying the solution I ran into another issue: Cat is an interface
export interface Cat extends Document {
//...
}
I get
Cat only refers to a type, but is being used as a value here
here
components: [
CatsService,
{
provide: 'CAT_MODEL_TOKEN',
useValue: Cat
}
]
Any ideas?
Hey @ArtworkAD
your Cat should not be an interface but must be a class which implement an interface.
For exemple to provide my User entity i do the following code:
export class User extends Model<User>
And then :
{
provide: 'UsersRepository',
useValue: User,
};
In fact you can't use an interface as a value.
@adrien2p but per https://docs.nestjs.com/recipes/mongodb the model is an interface
export interface Cat extends Document {
readonly name: string;
readonly age: number;
readonly breed: string;
}
@ArtworkAD
I think you need to make a Class for Cat
Then make it extends from that Interface CatInterface
export class Cat extends Model<CatInterface> {}
@shekohex I get
no base constructor has the specified number of type arguments
Could you past your code ?
@adrien2p the code is exactly as in https://github.com/nestjs/nest/issues/194#issuecomment-338262944 with Cat being an Interface as described in the documentation https://docs.nestjs.com/recipes/mongodb
When I try your suggestion export class User extends Model<User>
import { Model } from "mongoose";
import { Cat } from "./interface/cat.interface";
export class CatModel extends Model<Cat> {}
I get a error
no base constructor has the specified number of type arguments
The example provide by @shekohex was just an example there is no constructor in your class ... it鈥檚 normal ?
For mongoose dis you try something like
export const catsProviders = [
{
provide: 'CatModelToken',
useFactory: (connection: Connection) => connection.model('Cat', CatSchema),
inject: ['DbConnectionToken'],
},
];
@adrien2p Yes I have this. I am not sure what constructor to provide. A basic constructor like
export class CatModel extends Model<Cat> {
constructor() {
super();
}
}
does not solve the problem. I still get no base constructor has the specified number of type arguments
Did you try my last proposal ?
@adrien2p yes, I am stuck with creating the CatModel, since I am not sure what constructor to provide. Besides that, introducing a CatModel only for testing seems wrong to me...
Please remove the model and try my last proposal
@adrien2p do you mean this:
export const catsProviders = [
{
provide: 'CatModelToken',
useFactory: (connection: Connection) => connection.model('Cat', CatSchema),
inject: ['DbConnectionToken'],
},
];
I have this in my code. How does this effect testing?
Because you can inject it without create a class for you model
@adrien2p
So this is the service that needs the catModel:
@Component()
export class CatService {
constructor(@Inject(CAT_MODEL_TOKEN) private readonly catModel: Model<Cat>) {}
// ...
}
and the test is giving me an error
const module = await Test.createTestingModule({
controllers: [CatController],
components: [
CatService
],
}).compile();
because dependencies of CatService are not resolved. And I have this
export const catsProviders = [
{
provide: 'CatModelToken',
useFactory: (connection: Connection) => connection.model('Cat', CatSchema),
inject: ['DbConnectionToken'],
},
];
correctly setup. How can
you can inject it without create a class for you model
solve this problem?
So now in your components array you can add after the catService like this
components: [catService, ...catsProviders]
@adrien2p finally 馃拑 problem solved, thank you so much.
Not sure if it's still relevant after a year, but when you are unit testing your controller, you don't want to worry about it's dependencies so you mock them out by doing:
class MockCatsService {
}
describe('CatsController', () => {
beforeEach(async () => {
catsController: CatsController;
catsService: CatsService;
const module = await Test.createTestingModule({
controllers: [CatsController],
providers: [
{ provide: CatsService, useClass: MockCatsService },
],
}).compile();
catsService = module.get<CatsService>(CatsService);
catsController = module.get<CatsController>(CatsController);
});
});
so when your CatsService changes you don't have to update it in every single place you use it.
@adrien2p finally 馃拑 problem solved, thank you so much.
Could you share how you fixed this. I am still getting issues while unit testing. I am getting "Nest can't resolve dependencies of my service". Below are my code:
export class DeviceService extends BaseService {
constructor(@InjectModel('devices') private readonly _deviceModel: Model<Device>) {
super();
}
async getDevices(group): Promise<any> {
try {
return await this._deviceModel.find({ Group: group }).exec();
} catch (error) {
return Promise.reject(error);
}
}
}
@Controller()
export class DeviceController {
constructor(private readonly deviceService: DeviceService) {
}
@Get(':group')
async getDevices(@Res() response, @Param('group') group): Promise<any> {
try {
const result = await this.deviceService.getDevices(group);
return response.send(result);
}
catch (err) {
return response.status(422).send(err);
}
}
}
describe('DeviceController', () => {
let deviceController: DeviceController;
let deviceService: DeviceService;
const response = {
send: (body?: any) => { },
status: (code: number) => response,
};
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [DeviceController],
providers: [DeviceService, ...deviceProviders],
}).compile();
deviceService = module.get<DeviceService>(DeviceService);
deviceController = module.get<DeviceController>(DeviceController);
});
describe('getDevices()', () => {
it('should return an array of devices', async () => {
const result = [{
Group: 'group_abc',
DeviceId: 'device_abc',
},
{
Group: 'group_xyz',
DeviceId: 'device_xyz',
}];
jest.spyOn(deviceService, 'getDevices').mockImplementation(() => result);
expect(await deviceController.getDevices(response, null)).toBe(result);
});
});
});
When I am running my code above, I am getting: "Nest can't resolve dependencies of the DeviceService (?). Please make sure that the argument at index [0] is available in the current context."
@univerze
Somewhat correct: actually you'd mock the entire file to get rid of the provider import in every test that uses the service.
jest.mock('/path/to/service', MockService);
MockService
@univerze
Somewhat correct: actually you'd mock the entire file to get rid of the provider import in every test that uses the service.```ts
jest.mock('/path/to/service', MockService);
Hi I don't want to mock services as I mentioned above for maximum coverage I only wanted to mock the external dependencies like mongoose connection.
I have this problem:
Nest can't resolve dependencies of the CardService (QuiCardService, ?, RequestCardRepository, CardRepository, AccountRepository, CustomerRepository). Please make sure that the argument at index [1] is available in the CardModule context.
When i trying run test.
My code:
describe("Card api", () => {
let app: INestApplication;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [CardModule],
components: [
CardService
]
})
.compile();
app = module.createNestApplication();
await app.init();
});
CardModule:
@Module({
imports: [TypeOrmModule.forFeature([Card, Customer, Account, RequestCard])],
providers: [CardService, QuiCardService],
exports: [CardService, QuiCardService],
controllers: [CardController],
})
export class CardModule { }
CardService:
@Injectable()
export class CardService {
private voltaQuiApi: AxiosInstance;
constructor(
@Inject(QuiCardService)
private readonly quiCardService: QuiCardService,
@InjectConnection()
private readonly connection: Connection,
@InjectRepository(RequestCard)
private readonly reqCardRepository: Repository
@InjectRepository(Card)
private readonly cardRepository: Repository
@InjectRepository(Account)
private readonly accountRepository: Repository
@InjectRepository(Customer)
private readonly customerRepository: Repository
) {
this.voltaQuiApi = axios.create({
baseURL: process.env.VOLTAQUI_URL,
auth: {
username: process.env.VOLTAQUI_USERNAME,
password: process.env.VOLTAQUI_PASSWORD
}
});
}
Can someone help me?
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
@adrien2p finally 馃拑 problem solved, thank you so much.