import { Test, TestingModule } from '@nestjs/testing'; import { lastValueFrom, Observable, of } from 'rxjs'; import { anyNumber, anyString, instance, mock, verify, when } from 'ts-mockito'; import { Post } from '../database/post.model'; import { CreatePostDto } from './create-post.dto'; import { PostController } from './post.controller'; import { PostService } from './post.service'; import { PostServiceStub } from './post.service.stub'; import { UpdatePostDto } from './update-post.dto'; import { createMock } from '@golevelup/ts-jest'; import { Response } from 'express'; describe('Post Controller', () => { describe('Replace PostService in provider(useClass: PostServiceStub)', () => { let controller: PostController; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ { provide: PostService, useClass: PostServiceStub, }, ], controllers: [PostController], }).compile(); controller = await module.resolve<PostController>(PostController); }); it('should be defined', () => { expect(controller).toBeDefined(); }); it('GET on /posts should return all posts', async () => { const posts = await lastValueFrom(controller.getAllPosts()); expect(posts.length).toBe(3); }); it('GET on /posts/:id should return one post ', (done) => { controller.getPostById('1').subscribe((data) => { expect(data._id).toEqual('1'); done(); }); }); it('POST on /posts should save post', async () => { const post: CreatePostDto = { title: 'test title', content: 'test content', }; const saved = await lastValueFrom( controller.createPost( post, createMock<Response>({ location: jest.fn().mockReturnValue({ status: jest.fn().mockReturnValue({ send: jest.fn().mockReturnValue({ headers: { location: '/posts/post_id' }, status: 201, }), }), }), }), ), ); // console.log(saved); expect(saved.status).toBe(201); }); it('PUT on /posts/:id should update the existing post', (done) => { const post: UpdatePostDto = { title: 'test title', content: 'test content', }; controller .updatePost( '1', post, createMock<Response>({ status: jest.fn().mockReturnValue({ send: jest.fn().mockReturnValue({ status: 204, }), }), }), ) .subscribe((data) => { expect(data.status).toBe(204); done(); }); }); it('DELETE on /posts/:id should delete post', (done) => { controller .deletePostById( '1', createMock<Response>({ status: jest.fn().mockReturnValue({ send: jest.fn().mockReturnValue({ status: 204, }), }), }), ) .subscribe((data) => { expect(data).toBeTruthy(); done(); }); }); it('POST on /posts/:id/comments', async () => { const result = await lastValueFrom( controller.createCommentForPost( 'testpost', { content: 'testcomment' }, createMock<Response>({ location: jest.fn().mockReturnValue({ status: jest.fn().mockReturnValue({ send: jest.fn().mockReturnValue({ headers: { location: '/posts/post_id/comments/comment_id' }, status: 201, }), }), }), }), ), ); expect(result.status).toBe(201); }); it('GET on /posts/:id/comments', async () => { const result = await lastValueFrom( controller.getAllCommentsOfPost('testpost'), ); expect(result.length).toBe(1); }); }); describe('Replace PostService in provider(useValue: fake object)', () => { let controller: PostController; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ { provide: PostService, useValue: { findAll: (_keyword?: string, _skip?: number, _limit?: number) => of<any[]>([ { _id: 'testid', title: 'test title', content: 'test content', }, ]), }, }, ], controllers: [PostController], }).compile(); controller = await module.resolve<PostController>(PostController); }); it('should get all posts(useValue: fake object)', async () => { const result = await lastValueFrom(controller.getAllPosts()); expect(result[0]._id).toEqual('testid'); }); }); describe('Replace PostService in provider(useValue: jest mocked object)', () => { let controller: PostController; let postService: PostService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ { provide: PostService, useValue: { constructor: jest.fn(), findAll: jest .fn() .mockImplementation( (_keyword?: string, _skip?: number, _limit?: number) => of<any[]>([ { _id: 'testid', title: 'test title', content: 'test content', }, ]), ), }, }, ], controllers: [PostController], }).compile(); controller = await module.resolve<PostController>(PostController); postService = module.get<PostService>(PostService); }); it('should get all posts(useValue: jest mocking)', async () => { const result = await lastValueFrom(controller.getAllPosts('test', 10, 0)); expect(result[0]._id).toEqual('testid'); expect(postService.findAll).toBeCalled(); expect(postService.findAll).lastCalledWith('test', 0, 10); }); }); describe('Mocking PostService using ts-mockito', () => { let controller: PostController; const mockedPostService: PostService = mock(PostService); beforeEach(async () => { controller = new PostController(instance(mockedPostService)); }); it('should get all posts(ts-mockito)', async () => { when( mockedPostService.findAll(anyString(), anyNumber(), anyNumber()), ).thenReturn( of([ { _id: 'testid', title: 'test title', content: 'content' }, ]) as Observable<Post[]>, ); const result = await lastValueFrom(controller.getAllPosts('', 10, 0)); expect(result.length).toEqual(1); expect(result[0].title).toBe('test title'); verify( mockedPostService.findAll(anyString(), anyNumber(), anyNumber()), ).once(); }); }); });