Skip to content
Cheryl M edited this page Jul 17, 2024 · 4 revisions

Important

Tests will need to be updated / added when you update/create a new endpoint

e2e Tests

All tests need to cover all the endpoints, and all possible responses. They should be organized like the below example

Tip

describe for each endpoint should include - METHOD /route optionally a short description of the route

  • it should contain the expected return, and the action,
  • arrange in ascending statuscode i.e. 200, 201, 400, 401,404 etc
  • use additional describe if needed
Example: Auth Controller e2e tests

image

Unit Tests

There are 2 unit tests for each module, the controller, and service unit tests (.controller.spec.ts, .service.spec.ts)

Tests required

  • expect controller to be defined
  • expect services to be defined
  • expect (mocked) functions to be defined
  • mocked functions to be called
  • All functions need to be tested ( 1 describe block per function)
  • All dependencies and functions inside the each main controller/service function should be mocked and tested with tobecalledWith() or tobeCalled()

Anything already tested in e2e will not need to be tested here, e.g different responses (401, 403, etc)

Note

In the service unit tests we use prisma mock

Sample controller unit test
describe("VoyagesController", () => {
    let controller: VoyagesController;
    const requestMock = {} as unknown as CustomRequest;
    const dtoMock = {
        voyageTeamId: 1,
        responses: [
            { questionId: 26, text: "All" },
            { questionId: 27, text: "Deploy app" },
        ],
    } as CreateVoyageProjectSubmissionDto;
    const mockVoyagesService = {
        submitVoyageProject: jest.fn((_requestMock, _dtoMock) => {
            return {
                id: 1,
                voyageTeamId: 1,
                responseGroupId: 1,
                createdAt: new Date(Date.now()),
            };
        }),
        getVoyageProjects: jest.fn(() => {
            return [
                {
                    id: 1,
                    voyageTeamId: 1,
                    responseGroupId: 1,
                    createdAt: new Date(Date.now()),
                },
                {
                    id: 2,
                    voyageTeamId: 2,
                    responseGroupId: 1,
                    createdAt: new Date(Date.now()),
                },
            ];
        }),
    };
    beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
            controllers: [VoyagesController],
            providers: [VoyagesService],
        })
            .overrideProvider(VoyagesService)
            .useValue(mockVoyagesService)
            .compile();
        controller = module.get<VoyagesController>(VoyagesController);
    });
    it("voyages controller should be defined", () => {
        expect(controller).toBeDefined();
    });
    it("voyages service should be defined", () => {
        expect(controller.submitVoyageProject).toBeDefined();
    });
    describe("createVoyageProjectSubmission", () => {
        it("should be defined", () => {
            expect(controller.submitVoyageProject).toBeDefined();
        });
        it("should submit a voyage project", async () => {
            expect(
                await controller.submitVoyageProject(requestMock, dtoMock),
            ).toEqual({
                id: expect.any(Number),
                voyageTeamId: expect.any(Number),
                responseGroupId: expect.any(Number),
                createdAt: expect.any(Date),
            });
            expect(mockVoyagesService.submitVoyageProject).toHaveBeenCalledWith(
                requestMock,
                dtoMock,
            );
        });
    });
    describe("should get all voyage project submissions", () => {
        it("should be defined", () => {
            expect(controller.getAllVoyageProjects).toBeDefined();
        });
        it("should get all voyage project submissions", async () => {
            const projects = await controller.getAllVoyageProjects();
            expect(projects).toHaveLength(2);
            expect(projects).toBeArray();
            expect(projects).toContainEqual({
                id: expect.any(Number),
                voyageTeamId: expect.any(Number),
                responseGroupId: expect.any(Number),
                createdAt: expect.any(Date),
            });
            expect(mockVoyagesService.getVoyageProjects).toHaveBeenCalledWith();
        });
    });
});
Sample service unit test
describe("VoyagesService", () => {
    let service: VoyagesService;
    const requestMock = {
        user: {
            userId: "d31315ef-93c8-488f-a3f6-cb2df0016738",
            email: "[email protected]",
            role: ["voyager"],
            voyageTeams: [
                {
                    teamId: 1,
                    memberId: 1,
                },
            ],
        },
    } as unknown as CustomRequest;
    const dtoMock = {
        voyageTeamId: 1,
        responses: [
            { questionId: 26, text: "All" },
            { questionId: 27, text: "Deploy app" },
        ],
    } as CreateVoyageProjectSubmissionDto;
    const mockGlobalService = {
        responseDtoToArray: jest.fn((_dtoMock) => {
            return [_dtoMock.responses];
        }),
        checkQuestionsInFormByTitle: jest.fn(),
    };
    beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
            providers: [
                VoyagesService,
                {
                    provide: PrismaService,
                    useValue: prismaMock,
                },
                GlobalService,
                {
                    provide: GlobalService,
                    useValue: mockGlobalService,
                },
            ],
        }).compile();
        service = module.get<VoyagesService>(VoyagesService);
    });
    it("should be defined", () => {
        expect(service).toBeDefined();
    });
    it("should be able to submit a voyage project", async () => {
        prismaMock.$transaction.mockResolvedValueOnce({
            id: 1,
            voyageTeamId: 1,
            responseGroupId: 1,
            createdAt: new Date(Date.now()),
        });
        expect(await service.submitVoyageProject(requestMock, dtoMock)).toEqual(
            {
                id: expect.any(Number),
                voyageTeamId: expect.any(Number),
                responseGroupId: expect.any(Number),
                createdAt: expect.any(Date),
            },
        );
        expect(mockGlobalService.responseDtoToArray).toHaveBeenCalledWith(
            dtoMock,
        );
        expect(
            mockGlobalService.checkQuestionsInFormByTitle,
        ).toHaveBeenCalled();
    });
    it("should get all voyage projects ", async () => {
        prismaMock.formResponseVoyageProject.findMany.mockResolvedValue([
            {
                id: 1,
                voyageTeamId: 1,
                responseGroupId: 1,
                createdAt: new Date(Date.now()),
                updatedAt: new Date(Date.now()),
            },
            {
                id: 2,
                voyageTeamId: 1,
                responseGroupId: 2,
                createdAt: new Date(Date.now()),
                updatedAt: new Date(Date.now()),
            },
        ]);
        const projects = await service.getVoyageProjects();
        expect(projects).toHaveLength(2);
        expect(projects).toBeArray();
        expect(projects).toContainEqual({
            id: expect.any(Number),
            voyageTeamId: expect.any(Number),
            responseGroupId: expect.any(Number),
            createdAt: expect.any(Date),
            updatedAt: expect.any(Date),
        });
    });
});

Useful Link(s):

StyleGuide

nikeshghimire77/unit-testing-styleguide: Suggested best practices when writing Jest unit tests in TypeScript (Angular)

Nestjs unit tests videos

Note

Both Marius and Anson have e2e tests video on NestJS which are worth looking at

NestJS Testing Tutorial | Unit and Integration Testing (Marius Espejo)
Anson the developer

Mocking transactions

https://github.com/prisma/prisma/discussions/14435
https://github.com/prisma/prisma/discussions/22865
https://stackoverflow.com/questions/73007221/how-to-unit-test-a-transaction-wrapped-function-in-prisma

Miscellaneous

NestJS - Unit and E2E Testing
Build Complete REST API Feature with Nest JS (Using Prisma and Postgresql) from Scratch - Beginner-friendly - PART 5
API with NestJS #102. Writing unit tests with Prisma
NestJS Testing Recipe: Mocking Prisma
NestJS unit testing: A how-to guide with examples
fn vs spyOn

Clone this wiki locally