Hi I have a follow code:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { INote, Note, NoteDocument } from 'src/db/note.schema';
import { NoteAuthorList, NoteDetail } from '../notes-model';
import { INotesQueryService } from './../inotes-query.service';
@Injectable()
export class NotesQueryService implements INotesQueryService {
constructor(@InjectModel(Note.name) private readonly model: Model<NoteDocument>) { }
public async findByAuthorAndLikeText(author: string, query?: string): Promise<NoteAuthorList[]> {
return query ? (await this.model.find({ author })
.find({ $text: { $search: query } })) :
(await this.model.find({ author }))
}
public async findById(id: string): Promise<NoteDetail> {
const document = await this.model.findById(id)
if (!document) {
throw new NotFoundException(`A nota com id ${id} não foi encontrada`)
}
return document
}
}
for this code I wrote a follow tests:
import { getModelToken } from "@nestjs/mongoose";
import { Test, TestingModule } from "@nestjs/testing";
import { Model } from "mongoose";
import { faker } from "@faker-js/faker";
import { INote, Note, NoteDocument } from "../../../../src/db/note.schema";
import { NotesQueryService } from "../../../../src/services/notes/impl/notes-query.service";
import { INotesQueryService } from "../../../../src/services/notes/inotes-query.service"
import { NOTES_SERVICE_TOKENS } from "../../../../src/services/notes/token/notes.token";
import { noteDocumentFactory } from "../../..//bots/services/notes-model.factory";
import { ObjectId } from "mongodb";
import { an } from "@faker-js/faker/dist/airline-D6ksJFwG";
describe('NoteQueryService', () => {
let queryService: INotesQueryService
const noteModelMock = {
findById: jest.fn(),
find: jest.fn()
} as jest.Mocked<Partial<Model<NoteDocument>>>;
const noteModelMock2 = {
find: jest.fn()
} as jest.Mocked<Partial<Model<NoteDocument>>>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{ provide: NOTES_SERVICE_TOKENS.QUERY_SERVICE, useClass: NotesQueryService },
{ provide: getModelToken(Note.name), useValue: noteModelMock }
]
}).compile();
queryService = module.get<INotesQueryService>(NOTES_SERVICE_TOKENS.QUERY_SERVICE)
})
it('when note is stored and try find it by id then return it', async () => {
const storedNote = noteDocumentFactory.build() as INote
noteModelMock.findById!.mockResolvedValue(storedNote as NoteDocument)
const actual = await queryService.findById(storedNote.id)
expect(actual).toEqual(storedNote)
expect(noteModelMock.findById).toHaveBeenCalledWith(storedNote.id);
})
it('when not isn`t stored and try find it by id then throw error', async () => {
const id = new ObjectId().toHexString()
noteModelMock.findById!.mockResolvedValue(undefined)
await expect(queryService.findById(id)).rejects.toThrow()
expect(noteModelMock.findById).toHaveBeenCalledWith(id);
})
it('when request filter by body then filter it', async () => { // this test I need to fix
const storedNotes = noteDocumentFactory.buildList(5) as INote[]
const query = faker.lorem.word()
const author = faker.lorem.word()
noteModelMock.find!.mockResolvedValue(noteModelMock2)
noteModelMock2.find!.mockResolvedValue(storedNotes)
console.log(noteModelMock.find({ author }))
queryService.findByAuthorAndLikeText(author, query)
expect(noteModelMock.find).toHaveBeenCalledWith({ $text: { $search: query } })
expect(noteModelMock.find).toHaveBeenCalledWith({ author })
expect(noteModelMock.find).toHaveBeenCalledTimes(2)
})
it('when query filter is null, empty or undefined then no call second filter', async () => {
const storedNotes = noteDocumentFactory.buildList(5) as INote[]
const author = faker.lorem.word()
noteModelMock.find!.mockResolvedValue(storedNotes)
await queryService.findByAuthorAndLikeText(author)
expect(noteModelMock.find).toHaveBeenCalledWith({ author })
expect(noteModelMock.find).toHaveBeenCalledTimes(1)
})
})
In a test 'when request filter by body then filter it' I need to mock a chain call by find() method in mongoose model, I try to use this solution for fix:
How do I mock and test chained function with jest?
but this solution a chained call dont't chained a same method, for my case how can I fix my test?
You have some approaches to do that, but I think with linked returned mocks using mockResolvedValueOnce
and catching these returns with mock.calls
will do the work.
it('when request filter by body then filter it', async () => { // this test I need to fix
const storedNotes = noteDocumentFactory.buildList(5) as INote[]
const query = faker.lorem.word()
const author = faker.lorem.word()
noteModelMock.find!.mockResolvedValueOnce(noteModelMock2).mockResolvedValueOnce(storedNotes)
queryService.findByAuthorAndLikeText(author, query)
expect(noteModelMock.find.mock.calls[1][0]).toHaveBeenCalledWith({ $text: { $search: query } })
expect(noteModelMock..find.mock.calls[0][0]).toHaveBeenCalledWith({ author })
expect(noteModelMock.find).toHaveBeenCalledTimes(2)
})