こちらの記事は前回の続きです。
【Typescript】Clean Architecture + DDDでAPIを実装してみた
Jestを導入する
本来ならTDD的にテストを書いてから実装するのですが、元々の目的がClean Architecture + DDDのAPIをつくるのが目的だったため、後からテストを導入することになりました。
APIはTypescriptで実装されているため、Jestを導入しました。また前回同様、本記事ではメンバー関連を中心に取り扱います。APIしか作ってないのでE2EはなくUnitテストのみです。
型のモックの実現
テストにおいて型のモックは型を抽象化するジェネリックを使って実現します。特に実値で書く必要のない値に関してはモックを使用しています。
1 2 3 | export function mock<T>(): T { return ({} as unknown) as T } |
Domainのテスト
ドメインのテストを書いていきます。
バリデーションやドメインへの変換処理などをテストしていきます。誕生日から年齢を取得するドメインメソッドのテストは現在日から取得しているため1年経つとテストが落ちるようになってしまうのが悩みどころです。上手い具合にテストできればいいのですが。。
・Member.spec.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | import { Member, MemberBirthday, MemberGender, MemberId, MemberName, MemberProfile } from '../Member' import { MemberRelation, MemberRelationAttribute } from '../MemberRelation' import { DepartmentId } from '../Department' import { PostId } from '../Post' import { EmploymentStatusId } from '../EmploymentStatus' describe('MemberDomain', () => { describe('MemberName #isMaxSize', () => { test('メンバー名の上限文字数チェックfalse', async () => { const memberName = new MemberName('test') expect(memberName.isMaxSize()).toEqual(false) }) test('メンバー名の上限文字数チェックtrue', async () => { const memberName = new MemberName('testtesttesttesttesttesttesttest') expect(memberName.isMaxSize()).toEqual(true) }) }) describe('MemberBirthday #getAge', () => { test('誕生日から年齢を取得', async () => { const memberBirthday = new MemberBirthday(new Date('2000/12/10')) expect(memberBirthday.getAge()).toEqual(21) }) }) describe('MemberGender #getName', () => { test('GenderIdから性別名を取得', async () => { const memberGender = new MemberGender(1) expect(memberGender.getName()).toEqual('男性') }) }) describe('MemberRelationAttribute #update', () => { test('更新用のMemberRelationドメインに変換する', async () => { const memberId = new MemberId(1) const memberRelationAttribute = new MemberRelationAttribute( new MemberProfile( new MemberName('test'), new MemberBirthday(new Date('1900/8/29')), new MemberGender(1) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const expected = new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test'), new MemberBirthday(new Date('1900/8/29')), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) expect(memberRelationAttribute.update(memberId)).toEqual(expected) }) }) describe('MemberRelation #from', () => { test('MemberRelationドメインに変換する', async () => { const id = 1 const name = 'test' const birthday = new Date('2000/12/10') const gender = 1 const departmentId = 1 const postId = 1 const employmentStatusId = 1 const expected = new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test'), new MemberBirthday(new Date('2000/12/10')), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) expect(MemberRelation.from( id, name, birthday, gender, departmentId, postId, employmentStatusId )).toEqual(expected) }) }) }) |
UseCaseのテスト
次にUseCaseのテストを書いていきます。
MemberPortをモックし、メソッドが呼び出されたら返却されるレスポンスを定義して返してあげるようにします。レスポンスデータはモックできるものはモックし、登録系などバリデーションするものは実値で書いてます。
・MemberUseCase.spec.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | import { MemberPort } from '../../port/MemberPort' import { MemberUseCase } from '../MemberUseCase' import "reflect-metadata" import { mock } from './helper' import { when } from 'jest-when' import { MemberRelation, MemberRelationAttribute, MemberRelations } from '../../domain/MemberRelation' import { Member, MemberId, MemberProfile } from '../../domain/Member' import { MemberName } from '../../domain/Member' import { MemberBirthday } from '../../domain/Member' import { MemberGender } from '../../domain/Member' import { DepartmentId } from '../../domain/Department' import { PostId } from '../../domain/Post' import { EmploymentStatusId } from '../../domain/EmploymentStatus' import { PaymentAmount, PaymentDate, PaymentType, Remark, SalaryAttribute } from '../../domain/Salary' const memberPort = mock<MemberPort>() describe('MemberUseCase', () => { describe('find', () => { test('memberIdから1人のメンバーを取得する', async () => { const memberId = mock<MemberId>() const expected = mock<MemberRelation>() const memberFindFn = jest.fn() when(memberFindFn).calledWith(memberId).mockReturnValue(expected) memberPort.find = memberFindFn const target = new MemberUseCase(memberPort) const result = await target.find(memberId) expect(memberFindFn).toHaveBeenCalledTimes(1) expect(result).toEqual(expected) }) }) describe('findAll', () => { test('全てのメンバーを取得する', async () => { const expected = mock<MemberRelations>() const memberFindAllFn = jest.fn() when(memberFindAllFn).mockReturnValue(expected) memberPort.findAll = memberFindAllFn const target = new MemberUseCase(memberPort) const result = await target.findAll() expect(memberFindAllFn).toHaveBeenCalledTimes(1) expect(result).toEqual(expected) }) }) describe('create', () => { test('メンバーを新規登録する', async () => { const member = new MemberRelationAttribute( new MemberProfile( new MemberName('test'), new MemberBirthday(new Date()), new MemberGender(1) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const memberCreateFn = jest.fn() memberPort.create = memberCreateFn const target = new MemberUseCase(memberPort) await target.create(member) expect(memberCreateFn).toHaveBeenCalledTimes(1) }) }) describe('createSalary', () => { test('メンバーの給与情報を登録する', async () => { const memberId = mock<MemberId>() const salary = new SalaryAttribute( new PaymentAmount(10000), new PaymentType('bonus'), new Remark('test'), new PaymentDate(new Date()) ) const memberCreateSalaryFn = jest.fn() memberPort.createSalary = memberCreateSalaryFn const target = new MemberUseCase(memberPort) await target.createSalary(salary, memberId) expect(memberCreateSalaryFn).toHaveBeenCalledTimes(1) }) }) describe('update', () => { test('メンバーを更新する', async () => { const memberId = mock<MemberId>() const targetMember = new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test'), new MemberBirthday(new Date()), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const updateMember = new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test'), new MemberBirthday(new Date()), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const memberRelationAttribute = new MemberRelationAttribute( new MemberProfile( new MemberName('test'), new MemberBirthday(new Date()), new MemberGender(1) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const memberUpdateFn = jest.fn() memberPort.update = memberUpdateFn const memberRelationAttributeUpdateFn = jest.spyOn(memberRelationAttribute, "update") when(memberPort.find).calledWith(memberId).mockResolvedValue(targetMember) when(memberRelationAttributeUpdateFn).calledWith(targetMember.member.id).mockReturnValue(updateMember) when(memberPort.update).calledWith(updateMember) const target = new MemberUseCase(memberPort) await target.update(memberId, memberRelationAttribute) expect(memberUpdateFn).toHaveBeenCalledTimes(1) }) }) describe('delete', () => { test('メンバーを削除する', async () => { const memberId = mock<MemberId>() const memberDeleteFn = jest.fn() memberPort.delete = memberDeleteFn const target = new MemberUseCase(memberPort) await target.delete(memberId) expect(memberDeleteFn).toHaveBeenCalledTimes(1) }) }) }) |
Gateway・Controllerのテスト
Gateway・Controllerのテストを書いていきます。これらのレイヤら主に変換処理を行うためテストが長くなりがちです。
やっていることはシンプルでGatewayだったらUseCaseから受け取ったドメインをDriver用にプリミティブなオブジェクトに変換して渡してあげ、Driverから受け取ったレスポンスデータをドメインに詰め直す処理をしています。テストとしてはそれらがちゃんと期待しているデータに変換され期待しているメソッドが呼び出されているかをアサートしています。
privateなメソッドに関してはtarget[“hoge”](fuga)のようにアクセスしてテストを行っています。
・MemberGateway.spec.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | import { mock } from './helper' import { when } from 'jest-when' import { MemberDriver, MemberEntity } from '../../driver/MemberDriver' import { MemberGateway } from '../MemberGateway' import { Member, MemberBirthday, MemberGender, MemberId, MemberName, MemberProfile } from '../../domain/Member' import { MemberRelation, MemberRelationAttribute, MemberRelations } from '../../domain/MemberRelation' import { DepartmentId } from '../../domain/Department' import { PostId } from '../../domain/Post' import { EmploymentStatusId } from '../../domain/EmploymentStatus' import { PaymentAmount, PaymentDate, PaymentType, Remark, SalaryAttribute } from '../../domain/Salary' const memberDriver = mock<MemberDriver>() describe('MemberGateway', () => { describe('find', () => { test('memberIdから1人のメンバーをDriverから取得してDomainに変換する', async () => { const memberId = new MemberId(1) const memberEntity: MemberEntity = { member: { id: 1, name: 'test', birthday: new Date("1900/8/29"), gender: 1 }, departmentId: 1, postId: 1, employmentStatusId: 1 } const expected = new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test'), new MemberBirthday(new Date("1900/8/29")), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const target = new MemberGateway(memberDriver) const memberFindFn = jest.fn() memberDriver.find = memberFindFn const convertMemberValueFn = jest.fn() target['convertMemberValue'] = convertMemberValueFn when(memberFindFn).calledWith(1).mockResolvedValue(memberEntity) when(convertMemberValueFn).calledWith(memberEntity).mockResolvedValue(expected) const result = await target.find(memberId) expect(memberFindFn).toHaveBeenCalledTimes(1) expect(convertMemberValueFn).toHaveBeenCalledTimes(1) expect(result).toEqual(expected) }) }) describe('findAll', () => { test('Driverから全てのメンバーを取得しDomainに変換する', async () => { const memberEntity: MemberEntity[] = [ { member: { id: 1, name: 'test1', birthday: new Date("1900/8/29"), gender: 1 }, departmentId: 1, postId: 1, employmentStatusId: 1 }, { member: { id: 2, name: 'test2', birthday: new Date("1900/8/29"), gender: 2 }, departmentId: 2, postId: 2, employmentStatusId: 2 } ] const expected = new MemberRelations( [ new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test1'), new MemberBirthday(new Date("1900/8/29")), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ), new MemberRelation( new Member( new MemberId(2), new MemberProfile( new MemberName('test2'), new MemberBirthday(new Date("1900/8/29")), new MemberGender(2) ) ), new DepartmentId(2), new PostId(2), new EmploymentStatusId(2) ) ] ) const target = new MemberGateway(memberDriver) const memberFindAllFn = jest.fn() memberDriver.findAll = memberFindAllFn when(memberFindAllFn).calledWith().mockResolvedValue(memberEntity) const result = await target.findAll() expect(memberFindAllFn).toHaveBeenCalledTimes(1) expect(result).toEqual(expected) }) }) describe('create', () => { test('メンバーを登録する', async () => { const member = new MemberRelationAttribute( new MemberProfile( new MemberName('test'), new MemberBirthday(new Date("1900/8/29")), new MemberGender(1) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const target = new MemberGateway(memberDriver) const memberCreateFn = jest.fn() memberDriver.create = memberCreateFn await target.create(member) expect(memberCreateFn).toHaveBeenCalledTimes(1) }) }) describe('createSalary', () => { test('メンバーの給与情報を登録する', async () => { const memberId = new MemberId(1) const salary = new SalaryAttribute( new PaymentAmount(10000), new PaymentType('bonus'), new Remark('test'), new PaymentDate(new Date()) ) const target = new MemberGateway(memberDriver) const memberCreateSalaryFn = jest.fn() memberDriver.createSalary = memberCreateSalaryFn await target.createSalary(salary, memberId) expect(memberCreateSalaryFn).toHaveBeenCalledTimes(1) }) }) describe('update', () => { test('メンバーを更新する', async () => { const member = new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test1'), new MemberBirthday(new Date("1900/8/29")), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const target = new MemberGateway(memberDriver) const memberUpdateFn = jest.fn() memberDriver.update = memberUpdateFn await target.update(member) expect(memberUpdateFn).toHaveBeenCalledTimes(1) }) }) describe('delete', () => { test('メンバーを削除する', async () => { const memberId = new MemberId(1) const target = new MemberGateway(memberDriver) const memberDeleteFn = jest.fn() memberDriver.delete = memberDeleteFn await target.delete(memberId) expect(memberDeleteFn).toHaveBeenCalledTimes(1) }) }) describe('convertMemberValue', () => { test('MemberValueドメインに変換する', async () => { const member: MemberEntity = { member: { id: 1, name: 'test', birthday: new Date("1900/8/29"), gender: 1 }, departmentId: 1, postId: 1, employmentStatusId: 1 } const expected = new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test'), new MemberBirthday(new Date("1900/8/29")), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const target = new MemberGateway(memberDriver) const result = await target["convertMemberValue"](member) expect(result).toEqual(expected) }) }) }) |
・MemberController.spec.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | import { mock } from './helper' import { when } from 'jest-when' import { Member, MemberBirthday, MemberGender, MemberId, MemberName, MemberProfile } from '../../domain/Member' import { MemberRelation, MemberRelationAttribute, MemberRelations } from '../../domain/MemberRelation' import { DepartmentId } from '../../domain/Department' import { PostId } from '../../domain/Post' import { EmploymentStatusId } from '../../domain/EmploymentStatus' import { MemberUseCase } from '../../usecases/MemberUseCase' import { CreateMemberParams, CreateMemberSalaryParams, MemberController, UpdateMemberParams } from '../MemberController' import { SalaryUseCase } from '../../usecases/SalaryUseCase' import { MemberSalary } from '../../domain/MemberSalary' import { PaymentAmount, PaymentDate, PaymentType, Remark, Salaries, Salary, SalaryAttribute, SalaryId } from '../../domain/Salary' const memberUseCase = mock<MemberUseCase>() const salaryUseCase = mock<SalaryUseCase>() describe('MemberController', () => { describe('findMember', () => { test('memberIdから1人のメンバーを取得する', async () => { const id = 1 const memberId = new MemberId(id) const memberRelation = new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test1'), new MemberBirthday(new Date('1900/8/29')), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const expected = { id: 1, name: 'test1', birthday: new Date('1900/8/29'), age: 121, gender: '男性', department_id: 1, post_id: 1, employment_status_id: 1 } const memberFindFn = jest.fn() memberUseCase.find = memberFindFn const target = new MemberController(memberUseCase, salaryUseCase) const serializeMemberForDetailFn = jest.fn() target['serializeMemberForDetail'] = serializeMemberForDetailFn when(memberFindFn).calledWith(memberId).mockResolvedValue(memberRelation) when(serializeMemberForDetailFn).calledWith(memberRelation).mockResolvedValue(expected) const result = await target.findMember(id) expect(memberFindFn).toHaveBeenCalledTimes(1) expect(serializeMemberForDetailFn).toHaveBeenCalledTimes(1) expect(result).toEqual(expected) }) }) describe('findMemberSalaries', () => { test('memberIdから1人のメンバーの給与情報を取得する', async () => { const id = 1 const memberId = new MemberId(id) const memberSalary = new MemberSalary( new Member( new MemberId(1), new MemberProfile( new MemberName('test'), new MemberBirthday(new Date()), new MemberGender(1) ) ), new Salaries( [ new Salary( new SalaryId(1), new SalaryAttribute( new PaymentAmount(10000), new PaymentType('salary'), new Remark('remark1'), new PaymentDate(new Date('2001/2/29')) ) ), new Salary( new SalaryId(2), new SalaryAttribute( new PaymentAmount(20000), new PaymentType('bonus'), new Remark('remark2'), new PaymentDate(new Date('2001/2/29')) ) ) ] ) ) const expected = { member_id: 1, member_name: 'test', salaries: [ { id: 1, payment_amount: 10000, payment_type: 'salary', remark: 'remark1', payment_date: new Date('2001/2/29'), }, { id: 2, payment_amount: 20000, payment_type: 'bonus', remark: 'remark2', payment_date: new Date('2001/2/29'), } ] } const target = new MemberController(memberUseCase, salaryUseCase) const memberFindMemberSalariesFn = jest.fn() salaryUseCase.findByMemberId = memberFindMemberSalariesFn const serializeMemberSalariesFn = jest.fn() target['serializeMemberSalaries'] = serializeMemberSalariesFn when(memberFindMemberSalariesFn).calledWith(memberId).mockResolvedValue(memberSalary) when(serializeMemberSalariesFn).calledWith(memberSalary).mockResolvedValue(expected) const result = await target.findMemberSalaries(id) expect(memberFindMemberSalariesFn).toHaveBeenCalledTimes(1) expect(serializeMemberSalariesFn).toHaveBeenCalledTimes(1) expect(result).toEqual(expected) }) }) describe('findAllMembers', () => { test('メンバー全員を取得する', async () => { const memberRelations = new MemberRelations( [ new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test1'), new MemberBirthday(new Date('1900/8/29')), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ), new MemberRelation( new Member( new MemberId(2), new MemberProfile( new MemberName('test2'), new MemberBirthday(new Date('1900/8/29')), new MemberGender(1) ) ), new DepartmentId(2), new PostId(2), new EmploymentStatusId(2) ) ] ) const expected = [ { name: 'test1', department_id: 1, post_id: 1, employment_status_id: 1 }, { name: 'test2', department_id: 2, post_id: 2, employment_status_id: 2 } ] const target = new MemberController(memberUseCase, salaryUseCase) const memberFindAllMembersFn = jest.fn() memberUseCase.findAll = memberFindAllMembersFn const serializeMemberForListFn = jest.fn() target['serializeMemberForList'] = serializeMemberForListFn when(memberFindAllMembersFn).calledWith().mockResolvedValue(memberRelations) when(serializeMemberForListFn).calledWith(memberRelations).mockResolvedValue(expected) const result = await target.findAllMembers() expect(memberFindAllMembersFn).toHaveBeenCalledTimes(1) expect(serializeMemberForListFn).toHaveBeenCalledTimes(1) expect(result).toEqual(expected) }) }) describe('createMember', () => { test('メンバーを登録する', async () => { const createParams = mock<CreateMemberParams>() const createMember = mock<MemberRelationAttribute>() const memberCreateFn = jest.fn() when(memberCreateFn).calledWith(createMember) memberUseCase.create = memberCreateFn const target = new MemberController(memberUseCase, salaryUseCase) await target.createMember(createParams) expect(memberCreateFn).toHaveBeenCalledTimes(1) }) }) describe('createMemberSalary', () => { test('メンバーの給与を更新する', async () => { const createSalaryParams = mock<CreateMemberSalaryParams>() const createSalary = mock<SalaryAttribute>() const memberId = mock<MemberId>() const createMemberSalaryFn = jest.fn() when(createMemberSalaryFn).calledWith(createSalary, memberId) memberUseCase.createSalary = createMemberSalaryFn const target = new MemberController(memberUseCase, salaryUseCase) await target.createMemberSalary(createSalaryParams) expect(createMemberSalaryFn).toHaveBeenCalledTimes(1) }) }) describe('updateMember', () => { test('メンバーを更新する', async () => { const id = mock<number>() const memberId = mock<MemberId>() const deleteMemberFn = jest.fn() when(deleteMemberFn).calledWith(memberId) memberUseCase.delete = deleteMemberFn const target = new MemberController(memberUseCase, salaryUseCase) await target.deleteMember(id) expect(deleteMemberFn).toHaveBeenCalledTimes(1) }) }) describe('deleteMember', () => { test('メンバーを削除する', async () => { const id = mock<number>() const updateMemberParams = mock<UpdateMemberParams>() const updateMember = mock<MemberRelationAttribute>() const memberId = mock<MemberId>() const updateMemberFn = jest.fn() when(updateMemberFn).calledWith(memberId, updateMember) memberUseCase.update = updateMemberFn const target = new MemberController(memberUseCase, salaryUseCase) await target.updateMember(id, updateMemberParams) expect(updateMemberFn).toHaveBeenCalledTimes(1) }) }) describe('serializeMemberForList', () => { test('メンバー一覧用にドメインをオブジェクトに変換する', async () => { const members = new MemberRelations( [ new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test1'), new MemberBirthday(new Date('1900/8/29')), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ), new MemberRelation( new Member( new MemberId(2), new MemberProfile( new MemberName('test2'), new MemberBirthday(new Date('1900/8/29')), new MemberGender(2) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) ] ) const expected = [ { name: 'test1', department_id: 1, post_id: 1, employment_status_id: 1 }, { name: 'test2', department_id: 1, post_id: 1, employment_status_id: 1 } ] const target = new MemberController(memberUseCase, salaryUseCase) const result = await target["serializeMemberForList"](members) expect(result).toEqual(expected) }) }) describe('serializeMemberForDetail', () => { test('メンバー詳細用にドメインをオブジェクトに変換する', async () => { const member = new MemberRelation( new Member( new MemberId(1), new MemberProfile( new MemberName('test1'), new MemberBirthday(new Date('1900/8/29')), new MemberGender(1) ) ), new DepartmentId(1), new PostId(1), new EmploymentStatusId(1) ) const expected = { id: 1, name: 'test1', birthday: new Date('1900/8/29'), age: 121, gender: '男性', department_id: 1, post_id: 1, employment_status_id: 1 } const target = new MemberController(memberUseCase, salaryUseCase) const result = await target["serializeMemberForDetail"](member) expect(result).toEqual(expected) }) }) describe('serializeMemberSalaries', () => { test('メンバー給与情報用にドメインをオブジェクトに変換する', async () => { const memberSalary = new MemberSalary( new Member( new MemberId(1), new MemberProfile( new MemberName('test'), new MemberBirthday(new Date()), new MemberGender(1) ) ), new Salaries( [ new Salary( new SalaryId(1), new SalaryAttribute( new PaymentAmount(10000), new PaymentType('salary'), new Remark('remark1'), new PaymentDate(new Date('2001/2/29')) ) ), new Salary( new SalaryId(2), new SalaryAttribute( new PaymentAmount(20000), new PaymentType('bonus'), new Remark('remark2'), new PaymentDate(new Date('2001/2/29')) ) ) ] ) ) const expected = { member_id: 1, member_name: 'test', salaries: [ { id: 1, payment_amount: 10000, payment_type: 'salary', remark: 'remark1', payment_date: new Date('2001/2/29') }, { id: 2, payment_amount: 20000, payment_type: 'bonus', remark: 'remark2', payment_date: new Date('2001/2/29') } ] } const target = new MemberController(memberUseCase, salaryUseCase) const result = await target["serializeMemberSalaries"](memberSalary) expect(result).toEqual(expected) }) }) }) |
テスト実行
最後にテスト実行していきます。テストが通ると以下のようにpassedにカウントされます。
1 2 3 4 5 6 7 8 9 10 11 | $ npm run test PASS src/domain/tests/Member.spec.ts PASS src/usecases/tests/MemberUseCase.spec.ts PASS src/controller/tests/MemberController.spec.ts PASS src/gateway/tests/MemberGateway.spec.ts Test Suites: 4 passed, 4 total Tests: 29 passed, 29 total Snapshots: 0 total Time: 2.212 s, es |