From 78a196deb06a6efb8d3fb1978e2590e2841c3153 Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Wed, 8 Jan 2025 23:24:03 +0900 Subject: [PATCH 01/11] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20build(package?= =?UTF-8?q?=EC=97=90=20jest-mock=20=EC=B6=94=EA=B0=80):=20jest-mock=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EB=A1=9C=20=EB=B3=B4=EB=8B=A4=20=EC=89=AC?= =?UTF-8?q?=EC=9A=B4=20=EB=AA=A8=ED=82=B9=20=EC=82=AC=EC=9A=A9=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pnpm-lock.yaml | 62 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 790d0233..da12e9b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -223,6 +223,9 @@ importers: ioredis: specifier: ^5.4.1 version: 5.4.1 + jest-mock: + specifier: ^29.7.0 + version: 29.7.0 reflect-metadata: specifier: ^0.2.0 version: 0.2.2 @@ -5779,7 +5782,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 20.17.6 + '@types/node': 22.9.1 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -5792,14 +5795,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.6 + '@types/node': 22.9.1 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.17.6)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@22.9.1)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -5864,7 +5867,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 20.17.6 + '@types/node': 22.9.1 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -5934,7 +5937,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.17.6 + '@types/node': 22.9.1 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -6541,7 +6544,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 20.17.6 + '@types/node': 22.9.1 '@types/cookie@0.4.1': {} @@ -6549,7 +6552,7 @@ snapshots: '@types/cors@2.8.17': dependencies: - '@types/node': 20.17.6 + '@types/node': 22.9.1 '@types/doctrine@0.0.9': {} @@ -6664,7 +6667,7 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 20.17.6 + '@types/node': 22.9.1 '@types/serve-static@1.15.7': dependencies: @@ -7690,7 +7693,7 @@ snapshots: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.17 - '@types/node': 20.17.6 + '@types/node': 22.9.1 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -8930,6 +8933,37 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@29.7.0(@types/node@22.9.1)(ts-node@10.9.2(@types/node@20.17.6)(typescript@5.6.3)): + dependencies: + '@babel/core': 7.26.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.9.1 + ts-node: 10.9.2(@types/node@20.17.6)(typescript@5.6.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -8964,7 +8998,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.17.6 + '@types/node': 22.9.1 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -9038,7 +9072,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.6 + '@types/node': 22.9.1 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -9066,7 +9100,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.6 + '@types/node': 22.9.1 chalk: 4.1.2 cjs-module-lexer: 1.4.1 collect-v8-coverage: 1.0.2 @@ -9112,7 +9146,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.17.6 + '@types/node': 22.9.1 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -9131,7 +9165,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.17.6 + '@types/node': 22.9.1 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 From 5c26a81ca187edb34f5bff716b0b4b0cb24e2e92 Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Wed, 8 Jan 2025 23:39:37 +0900 Subject: [PATCH 02/11] =?UTF-8?q?=F0=9F=A7=AAtest(chat.service.spec.ts):?= =?UTF-8?q?=20chat=20service=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/chat/chat.service.spec.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/server/src/chat/chat.service.spec.ts b/server/src/chat/chat.service.spec.ts index 110cd7d3..285251e5 100644 --- a/server/src/chat/chat.service.spec.ts +++ b/server/src/chat/chat.service.spec.ts @@ -1,18 +1,19 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ChatService } from './chat.service'; +import { ChatRepository } from './chat.repository'; +import { ModuleMocker } from 'jest-mock'; + +const moduleMocker = new ModuleMocker(global); describe('ChatService', () => { - let service: ChatService; + let chatService: ChatService; + let chatRepository: ChatRepository; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ChatService], }).compile(); - service = module.get(ChatService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); + chatService = module.get(ChatService); }); }); From b06fb06446fec1caa3afe10ec284702e855c5e14 Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Thu, 9 Jan 2025 00:19:15 +0900 Subject: [PATCH 03/11] =?UTF-8?q?=F0=9F=A7=AAtest(chat.service.spec.ts):?= =?UTF-8?q?=20chat=20service=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/chat/chat.service.spec.ts | 58 +++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/server/src/chat/chat.service.spec.ts b/server/src/chat/chat.service.spec.ts index 285251e5..feb53c63 100644 --- a/server/src/chat/chat.service.spec.ts +++ b/server/src/chat/chat.service.spec.ts @@ -1,19 +1,65 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ChatService } from './chat.service'; import { ChatRepository } from './chat.repository'; -import { ModuleMocker } from 'jest-mock'; - -const moduleMocker = new ModuleMocker(global); +import { describe } from 'node:test'; +import { BadRequestException, PlayerNotFoundException } from '../exceptions/game.exception'; +import { Player } from '../common/types/game.types'; +import { PlayerRole, PlayerStatus } from '../common/enums/game.status.enum'; describe('ChatService', () => { let chatService: ChatService; - let chatRepository: ChatRepository; + + const mockChatRepository = { + getPlayer: jest.fn(), + existsRoom: jest.fn(), + }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [ChatService], + providers: [ChatService, { provide: ChatRepository, useValue: mockChatRepository }], }).compile(); - chatService = module.get(ChatService); + chatService = module.get(ChatService); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('sendMessage 테스트', async () => { + it('메시지가 공백일 때', async () => { + await expect(async () => { + await chatService.sendMessage('room1', 'player1', ''); + }).rejects.toThrowError(BadRequestException); + }); + + it('플레이어가 존재하지 않을 때', async () => { + mockChatRepository.getPlayer.mockResolvedValue(null); + + await expect(async () => { + await chatService.sendMessage('room1', 'player1', 'hello world'); + }).rejects.toThrowError(PlayerNotFoundException); + + // 에러 캐치로 인해 순서를 바꿔서 배치 + expect(mockChatRepository.getPlayer).toHaveBeenCalled(); + }); + + it('플레이어가 정상적으로 존재할 때', async () => { + const player: Player = { + playerId: 'player1', + role: PlayerRole.GUESSER, + status: PlayerStatus.PLAYING, + nickname: 'player', + profileImage: null, + score: 10, + }; + mockChatRepository.getPlayer.mockResolvedValue(player); + + const result = await chatService.sendMessage('room1', 'player1', 'hello world'); + + // TODO : 타입 narrowing 필요 + expect(result).toBeDefined(); + expect(mockChatRepository.getPlayer).toHaveBeenCalled(); + }); }); }); From aaaebce3ae52e60db8ab49ac360368ab83934826 Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Thu, 9 Jan 2025 00:20:34 +0900 Subject: [PATCH 04/11] =?UTF-8?q?=E2=9C=A8feat(server/packages,=20tsconfig?= =?UTF-8?q?):=20jest=20=EC=A0=88=EB=8C=80=EA=B2=BD=EB=A1=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 절대경로로 제대로 실행되지 않는 문제 해결 notion에 작성했습니다~ --- server/package.json | 6 +++++- server/tsconfig.json | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/server/package.json b/server/package.json index 93f97d4f..1145a838 100644 --- a/server/package.json +++ b/server/package.json @@ -29,6 +29,7 @@ "@troublepainter/core": "workspace:*", "axios": "^1.7.7", "ioredis": "^5.4.1", + "jest-mock": "^29.7.0", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "socket.io": "^4.8.1", @@ -75,6 +76,9 @@ "**/*.(t|j)s" ], "coverageDirectory": "../coverage", - "testEnvironment": "node" + "testEnvironment": "node", + "moduleNameMapper": { + "^src/(.*)$": "/$1" + } } } diff --git a/server/tsconfig.json b/server/tsconfig.json index 95f5641c..1efd6fec 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -17,5 +17,8 @@ "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false + }, + "paths": { + "src/*" : ["./src/*"] } } From 079a0345295cd6bbee51ac92f35b3095c072e421 Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Thu, 9 Jan 2025 00:48:12 +0900 Subject: [PATCH 05/11] =?UTF-8?q?=F0=9F=A7=AAtest(server/chat.repository.s?= =?UTF-8?q?pec.ts):=20repository=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 각각의 임계 조건에 대해 검증하는 테스트 추가 --- server/src/chat/chat.repository.spec.ts | 94 +++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 server/src/chat/chat.repository.spec.ts diff --git a/server/src/chat/chat.repository.spec.ts b/server/src/chat/chat.repository.spec.ts new file mode 100644 index 00000000..4faa5522 --- /dev/null +++ b/server/src/chat/chat.repository.spec.ts @@ -0,0 +1,94 @@ +import { ChatRepository } from './chat.repository'; +import { Test, TestingModule } from '@nestjs/testing'; +import { RedisService } from '../redis/redis.service'; +import { describe } from 'node:test'; +import { PlayerRole } from '../common/enums/game.status.enum'; + +describe('chat repository tests', () => { + let chatRepository: ChatRepository; + + const mockRedisService = { + hgetall: jest.fn(), + exists: jest.fn(), + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + ChatRepository, + { + provide: RedisService, + useValue: mockRedisService, + }, + ], + }).compile(); + + chatRepository = module.get(ChatRepository); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('getPlayer 테스트', () => { + it('플레이어 데이터가 없을 때 null을 리턴', async () => { + mockRedisService.hgetall.mockResolvedValue(null); + + const result = await chatRepository.getPlayer('room1', 'player1'); + expect(mockRedisService.hgetall).toHaveBeenCalled(); + expect(result).toBeNull(); + }); + + it('플레이어 데이터가 있을 때 Player로 데이터를 변환해 리턴', async () => { + const player = { + role: PlayerRole.GUESSER, + userImg: '', + score: 15, + }; + + mockRedisService.hgetall.mockResolvedValue(player); + + const result = await chatRepository.getPlayer('room1', 'player1'); + expect(result).toEqual({ + ...player, + profileImage: null, + }); + }); + }); + + describe('existsRoom 테스트', () => { + it('방이 존재하지 않을 때 false를 리턴', async () => { + mockRedisService.exists.mockResolvedValue(0); + + const result = await chatRepository.existsRoom('room1'); + expect(mockRedisService.exists).toHaveBeenCalled(); + expect(result).toBe(false); + }); + }); + + it('방이 존재할 때 true를 리턴', async () => { + mockRedisService.exists.mockResolvedValue(1); + + const result = await chatRepository.existsRoom('room1'); + expect(mockRedisService.exists).toHaveBeenCalled(); + expect(result).toBe(true); + }); + + describe('existsPlayer 테스트', () => { + it('플레이어가 존재하지 않을 때 false를 리턴', async () => { + mockRedisService.exists.mockResolvedValue(0); + + const result = await chatRepository.existsPlayer('room1', 'player1'); + expect(mockRedisService.exists).toHaveBeenCalled(); + expect(result).toBe(false); + }); + + it('플레이어가 존재할 때 true를 리턴한다', async () => { + mockRedisService.exists.mockResolvedValue(1); + + const result = await chatRepository.existsPlayer('room1', 'player1'); + expect(mockRedisService.exists).toHaveBeenCalled(); + expect(result).toBe(true); + }); + }); +}); From b86e52e7c99668d32c560bc5ffc1ef51eb0c75a5 Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Thu, 9 Jan 2025 00:53:01 +0900 Subject: [PATCH 06/11] =?UTF-8?q?=F0=9F=A7=AAtest(server/chat.gateway.spec?= =?UTF-8?q?.ts):=20toThrow=EC=82=AC=EC=9A=A9=ED=95=B4=20error=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC,=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=EB=AC=B8?= =?UTF-8?q?=20=EC=9D=BC=EA=B4=80=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit toThrowError는 deprecated되어 toThrow를 사용해 변경 --- server/src/chat/chat.service.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/chat/chat.service.spec.ts b/server/src/chat/chat.service.spec.ts index feb53c63..323a452d 100644 --- a/server/src/chat/chat.service.spec.ts +++ b/server/src/chat/chat.service.spec.ts @@ -27,18 +27,18 @@ describe('ChatService', () => { }); describe('sendMessage 테스트', async () => { - it('메시지가 공백일 때', async () => { + it('메시지가 공백일 때 BadRequestException 발생', async () => { await expect(async () => { await chatService.sendMessage('room1', 'player1', ''); - }).rejects.toThrowError(BadRequestException); + }).rejects.toThrow(BadRequestException); }); - it('플레이어가 존재하지 않을 때', async () => { + it('플레이어가 존재하지 않을 때 PlayerNotFoundException 발생', async () => { mockChatRepository.getPlayer.mockResolvedValue(null); await expect(async () => { await chatService.sendMessage('room1', 'player1', 'hello world'); - }).rejects.toThrowError(PlayerNotFoundException); + }).rejects.toThrow(PlayerNotFoundException); // 에러 캐치로 인해 순서를 바꿔서 배치 expect(mockChatRepository.getPlayer).toHaveBeenCalled(); From 57ec421e50e5598b428c5fe2d7feb32741bc28d7 Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Thu, 9 Jan 2025 00:54:23 +0900 Subject: [PATCH 07/11] =?UTF-8?q?=F0=9F=A7=AAtest(server/chat.gateway.spec?= =?UTF-8?q?.ts):=20gateway=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit as unknown as 를 이용해 Socket 타입 문제 해결, 각각의 if나 throw에 대해 테스트 작성 --- server/src/chat/chat.gateway.spec.ts | 89 ++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/server/src/chat/chat.gateway.spec.ts b/server/src/chat/chat.gateway.spec.ts index 34daca94..9c3cf3a0 100644 --- a/server/src/chat/chat.gateway.spec.ts +++ b/server/src/chat/chat.gateway.spec.ts @@ -1,18 +1,99 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ChatGateway } from './chat.gateway'; +import { ChatService } from './chat.service'; +import { BadRequestException, PlayerNotFoundException, RoomNotFoundException } from 'src/exceptions/game.exception'; +import { Socket } from 'socket.io'; describe('ChatGateway', () => { let gateway: ChatGateway; + let mockSocket: Partial; + + const mockChatService = { + existsRoom: jest.fn(), + existsPlayer: jest.fn(), + sendMessage: jest.fn(), + }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [ChatGateway], + providers: [ChatGateway, { provide: ChatService, useValue: mockChatService }], }).compile(); - gateway = module.get(ChatGateway); + gateway = module.get(ChatGateway); + + /** + * as unknown as 를 이용해 타입을 먼저 unknown으로 바꾼 다음, + * 원하는 타입에 type assertion을 한다. + */ + mockSocket = { + handshake: { auth: { roomId: 'room1', playerId: 'player1' } }, + data: {}, + join: jest.fn(), + to: jest.fn().mockReturnThis(), + emit: jest.fn(), + } as unknown as Socket; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('handleConnection 테스트', () => { + it('roomId가 null일 때 BadRequestException을 발생', () => { + mockSocket.handshake.auth = { roomId: null }; + + expect(() => gateway.handleConnection(mockSocket as Socket)).toThrow(BadRequestException); + }); + + it('playerId가 null일 때 BadRequestException을 발생', () => { + mockSocket.handshake.auth = { playerId: null }; + + expect(() => gateway.handleConnection(mockSocket as Socket)).toThrow(BadRequestException); + }); + + it('room이 존재하지 않을 때 RoomNotFoundException을 발생', () => { + mockChatService.existsRoom.mockReturnValue(false); + + expect(() => gateway.handleConnection(mockSocket as Socket)).toThrow(RoomNotFoundException); + expect(mockChatService.existsRoom).toHaveBeenCalled(); + }); + + it('플레이어가 룸에 존재하지 않을 때 PlayerNotFoundException을 발생', () => { + mockChatService.existsRoom.mockReturnValue(true); + mockChatService.existsPlayer.mockReturnValue(false); + + expect(() => gateway.handleConnection(mockSocket as Socket)).toThrow(PlayerNotFoundException); + expect(mockChatService.existsPlayer).toHaveBeenCalled(); + }); + + it('플레이어와 방이 정상적으로 할당되어 있을 때', () => { + mockChatService.existsRoom.mockReturnValue(true); + mockChatService.existsPlayer.mockReturnValue(true); + + gateway.handleConnection(mockSocket as Socket); + + expect(mockSocket.join).toHaveBeenCalled(); + expect(mockSocket.data).toEqual({ roomId: 'room1', playerId: 'player1' }); + }); }); - it('should be defined', () => { - expect(gateway).toBeDefined(); + describe('handleSendMessage 테스트', () => { + it('데이터가 없을 때 BadRequestException을 발생', async () => { + mockSocket.data = {}; + + await expect(gateway.handleSendMessage(mockSocket as Socket, { message: 'hello world' })).rejects.toThrow( + BadRequestException, + ); + }); + + it('정상적으로 메시지를 발신할 수 있을 때', async () => { + mockSocket.data = { roomId: 'room1', playerId: 'player1' }; + mockChatService.sendMessage.mockResolvedValue({ message: 'hello world', sender: 'player1' }); + + await gateway.handleSendMessage(mockSocket as Socket, { message: 'hello world' }); + + expect(mockChatService.sendMessage).toHaveBeenCalled(); + expect(mockSocket.to('room1').emit).toHaveBeenCalled(); + }); }); }); From f37389cd24d110432fa4baefb3f20a3aa9434cf3 Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Thu, 9 Jan 2025 09:50:03 +0900 Subject: [PATCH 08/11] =?UTF-8?q?=F0=9F=A7=AAtest(server/chat.module.spec.?= =?UTF-8?q?ts):=20=EB=AA=A8=EB=93=88=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit module 컴파일 후 imports, providers를 체크하는 방식으로 테스트 --- server/src/chat/chat.module.spec.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 server/src/chat/chat.module.spec.ts diff --git a/server/src/chat/chat.module.spec.ts b/server/src/chat/chat.module.spec.ts new file mode 100644 index 00000000..bc9367f1 --- /dev/null +++ b/server/src/chat/chat.module.spec.ts @@ -0,0 +1,20 @@ +import { ChatModule } from './chat.module'; +import { Test } from '@nestjs/testing'; +import { ChatService } from './chat.service'; +import { ChatRepository } from './chat.repository'; +import { RedisModule } from '../redis/redis.module'; +import { ChatGateway } from './chat.gateway'; + +describe('ChatModule', () => { + it('컴파일 확인', async () => { + const module = await Test.createTestingModule({ + imports: [ChatModule], + }).compile(); + + expect(module).toBeDefined(); + expect(module.get(ChatService)).toBeInstanceOf(ChatService); + expect(module.get(ChatRepository)).toBeInstanceOf(ChatRepository); + expect(module.get(ChatGateway)).toBeInstanceOf(ChatGateway); + expect(module.get(RedisModule)).toBeInstanceOf(RedisModule); + }); +}); From 9df25b78bc3e02788e7832aff57e5b59aa5ee13d Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Thu, 9 Jan 2025 09:53:26 +0900 Subject: [PATCH 09/11] =?UTF-8?q?=F0=9F=A7=AAtest(server/chat.repository.s?= =?UTF-8?q?pec.ts):=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B6=80=EC=A1=B1?= =?UTF-8?q?=ED=95=9C=20=EB=B6=80=EB=B6=84=20=EC=B6=94=EA=B0=80,=20describe?= =?UTF-8?q?=20=EB=8B=A4=EB=A5=B8=20=EA=B3=B3=EA=B3=BC=20=ED=86=B5=EC=9D=BC?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/chat/chat.repository.spec.ts | 64 ++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/server/src/chat/chat.repository.spec.ts b/server/src/chat/chat.repository.spec.ts index 4faa5522..a3d5fe17 100644 --- a/server/src/chat/chat.repository.spec.ts +++ b/server/src/chat/chat.repository.spec.ts @@ -4,7 +4,7 @@ import { RedisService } from '../redis/redis.service'; import { describe } from 'node:test'; import { PlayerRole } from '../common/enums/game.status.enum'; -describe('chat repository tests', () => { +describe('ChatRepository', () => { let chatRepository: ChatRepository; const mockRedisService = { @@ -39,7 +39,24 @@ describe('chat repository tests', () => { expect(result).toBeNull(); }); - it('플레이어 데이터가 있을 때 Player로 데이터를 변환해 리턴', async () => { + it('플레이어 데이터 중 role이 없을 때 null을 리턴', async () => { + const player = { + role: '', + userImg: 'test', + score: 15, + }; + + mockRedisService.hgetall.mockResolvedValue(player); + + const result = await chatRepository.getPlayer('room1', 'player1'); + expect(result).toEqual({ + ...player, + role: null, + profileImage: player.userImg, + }); + }); + + it('플레이어 데이터 중 이미지가 없을 때 profileImage가 null을 리턴', async () => { const player = { role: PlayerRole.GUESSER, userImg: '', @@ -54,6 +71,49 @@ describe('chat repository tests', () => { profileImage: null, }); }); + + it('플레이어 데이터 중 이미지가 있을 때 profileImage가 userImg와 동일하게 리턴', async () => { + const player = { + role: PlayerRole.GUESSER, + userImg: 'test', + score: 15, + }; + + mockRedisService.hgetall.mockResolvedValue(player); + + const result = await chatRepository.getPlayer('room1', 'player1'); + expect(result).toEqual({ + ...player, + profileImage: player.userImg, + }); + }); + + it('플레이어 데이터에서 score 임계값을 처리', async () => { + const testCases = [ + { inputScore: '15', expectedScore: 15 }, + { inputScore: '-5', expectedScore: -5 }, + { inputScore: '', expectedScore: 0 }, + { inputScore: null, expectedScore: 0 }, + { inputScore: 'abc', expectedScore: 0 }, + ]; + + for (const testCase of testCases) { + const player = { + role: PlayerRole.GUESSER, + userImg: 'test', + score: testCase.inputScore, + }; + + mockRedisService.hgetall.mockResolvedValue(player); + + const result = await chatRepository.getPlayer('room1', 'player1'); + expect(result).toEqual({ + ...player, + profileImage: player.userImg, + score: testCase.expectedScore, + }); + } + }); }); describe('existsRoom 테스트', () => { From eb1b7163e2ec1418effc1af18529702670ae4def Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Thu, 9 Jan 2025 09:54:13 +0900 Subject: [PATCH 10/11] =?UTF-8?q?=F0=9F=A7=AAtest(server/chat.service.spec?= =?UTF-8?q?.ts):=20chat.service=20existsRoom,=20existsPlayer=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/chat/chat.service.spec.ts | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/server/src/chat/chat.service.spec.ts b/server/src/chat/chat.service.spec.ts index 323a452d..a075956b 100644 --- a/server/src/chat/chat.service.spec.ts +++ b/server/src/chat/chat.service.spec.ts @@ -12,6 +12,7 @@ describe('ChatService', () => { const mockChatRepository = { getPlayer: jest.fn(), existsRoom: jest.fn(), + existsPlayer: jest.fn(), }; beforeEach(async () => { @@ -62,4 +63,40 @@ describe('ChatService', () => { expect(mockChatRepository.getPlayer).toHaveBeenCalled(); }); }); + + describe('existsRoom 테스트', () => { + it('존재하는 방일 때 true를 리턴', async () => { + mockChatRepository.existsRoom.mockResolvedValue(true); + + const result = await chatService.existsRoom('room1'); + expect(result).toBe(true); + expect(mockChatRepository.existsRoom).toHaveBeenCalledWith('room1'); + }); + + it('존재하지 않는 방일 때 false를 리턴', async () => { + mockChatRepository.existsRoom.mockResolvedValue(false); + + const result = await chatService.existsRoom('room2'); + expect(result).toBe(false); + expect(mockChatRepository.existsRoom).toHaveBeenCalledWith('room2'); + }); + }); + + describe('existsPlayer 테스트', () => { + it('존재하는 플레이어일 때 true를 리턴', async () => { + mockChatRepository.existsPlayer.mockResolvedValue(true); + + const result = await chatService.existsPlayer('room1', 'player1'); + expect(result).toBe(true); + expect(mockChatRepository.existsPlayer).toHaveBeenCalledWith('room1', 'player1'); + }); + + it('존재하지 않는 플레이어일 때 false를 리턴', async () => { + mockChatRepository.existsPlayer.mockResolvedValue(false); + + const result = await chatService.existsPlayer('room1', 'player2'); + expect(result).toBe(false); + expect(mockChatRepository.existsPlayer).toHaveBeenCalledWith('room1', 'player2'); + }); + }); }); From fa53adc5cdc95bd7dd37e966e1b4c551c151f9e1 Mon Sep 17 00:00:00 2001 From: gimseonghwan Date: Thu, 9 Jan 2025 09:55:46 +0900 Subject: [PATCH 11/11] =?UTF-8?q?=F0=9F=93=9Ddocs(chat.service.spec.ts):?= =?UTF-8?q?=20todo=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/chat/chat.service.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/chat/chat.service.spec.ts b/server/src/chat/chat.service.spec.ts index a075956b..a14b32c2 100644 --- a/server/src/chat/chat.service.spec.ts +++ b/server/src/chat/chat.service.spec.ts @@ -58,7 +58,6 @@ describe('ChatService', () => { const result = await chatService.sendMessage('room1', 'player1', 'hello world'); - // TODO : 타입 narrowing 필요 expect(result).toBeDefined(); expect(mockChatRepository.getPlayer).toHaveBeenCalled(); });