Merged PR 327: テストを最新化(ユーザー追加)

## 概要
[Task2400: テストを最新化(ユーザー追加)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2400)

- 新規ユーザー追加のユニットテストをDBを利用するテストへ置き換え
- API呼び出しやRepository呼び出しをMockする仕組みを改善
  - オブジェクト全てを上書きするのではなく、特定のメソッドのみを上書きする形に修正
    - これにより、他のDBアクセスは正常にSQLiteへのアクセスが成功しつつ、特定のDBアクセスのみMockにできる

## レビューポイント
- テストケースは足りているか
- Mockする仕組みを改善したが、修正内容は問題ないか

## 動作確認状況
- npm run test で成功
This commit is contained in:
湯本 開 2023-08-16 02:54:32 +00:00
parent 86d11e7447
commit 704a5aafc2
5 changed files with 814 additions and 335 deletions

View File

@ -1,4 +1,7 @@
import { ConflictError } from '../../gateways/adb2c/adb2c.service';
import { User, newUser } from '../../repositories/users/entity/user.entity';
// ### ユニットテスト用コード以外では絶対に使用してはいけないダーティな手段を使用しているが、他の箇所では使用しないこと ###
/**
* adB2cServiceのモックを作成してTServiceが依存するサービス(adB2cService)
@ -17,11 +20,14 @@ export const overrideAdB2cService = <TService>(
},
): void => {
const { createUser } = overrides;
Object.defineProperty(service, 'adB2cService', {
value: {
createUser: createUser ?? jest.fn().mockResolvedValue({ sub: 'dummy' }),
},
});
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
const obj = (service as any).adB2cService;
if (createUser) {
Object.defineProperty(obj, createUser.name, {
value: createUser,
writable: true,
});
}
};
/**
@ -38,6 +44,11 @@ export const overrideSendgridService = <TService>(
userId: number,
email: string,
) => Promise<{ subject: string; text: string; html: string }>;
createMailContentFromEmailConfirmForNormalUser?: (
accountId: number,
userId: number,
email: string,
) => Promise<{ subject: string; text: string; html: string }>;
sendMail?: (
to: string,
from: string,
@ -47,17 +58,59 @@ export const overrideSendgridService = <TService>(
) => Promise<void>;
},
): void => {
const { createMailContentFromEmailConfirm, sendMail } = overrides;
Object.defineProperty(service, 'sendgridService', {
value: {
createMailContentFromEmailConfirm:
createMailContentFromEmailConfirm ??
jest.fn().mockResolvedValue({
subject: 'dummySubject',
text: 'dummyText',
html: 'dummyHtml',
}),
sendMail: sendMail ?? jest.fn().mockResolvedValue(undefined),
},
});
const {
createMailContentFromEmailConfirm,
createMailContentFromEmailConfirmForNormalUser,
sendMail,
} = overrides;
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
const obj = (service as any).sendgridService;
if (sendMail) {
Object.defineProperty(obj, sendMail.name, {
value: sendMail,
writable: true,
});
}
if (createMailContentFromEmailConfirm) {
Object.defineProperty(obj, createMailContentFromEmailConfirm.name, {
value: createMailContentFromEmailConfirm,
writable: true,
});
}
if (createMailContentFromEmailConfirmForNormalUser) {
Object.defineProperty(
obj,
createMailContentFromEmailConfirmForNormalUser.name,
{
value: createMailContentFromEmailConfirmForNormalUser,
writable: true,
},
);
}
};
/**
* usersRepositoryのモックを作成してTServiceが依存するサービス(usersRepositoryService)
* serviceに指定するオブジェクトは`usersRepository: UsersRepositoryService`
* @param service TService
* @param overrides usersRepositoryの各種メソッドのモックが返す値
*/
export const overrideUsersRepositoryService = <TService>(
service: TService,
overrides: {
createNormalUser?: (user: newUser) => Promise<User>;
},
): void => {
const { createNormalUser } = overrides;
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
const obj = (service as any).usersRepository;
if (createNormalUser) {
Object.defineProperty(obj, createNormalUser.name, {
value: createNormalUser,
writable: true,
});
}
};

View File

@ -137,7 +137,7 @@ describe('createAccount', () => {
expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR);
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
} else {
expect(true).toBe(false); // ここには来てはいけない
fail();
}
}
@ -186,7 +186,7 @@ describe('createAccount', () => {
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
expect(e.getResponse()).toEqual(makeErrorResponse('E010301'));
} else {
expect(true).toBe(false); // ここには来てはいけない
fail();
}
}

View File

@ -38,6 +38,69 @@ import { AdB2cMockValue, makeAdB2cServiceMock } from './users.service.mock';
import { AdB2cService } from '../../../gateways/adb2c/adb2c.service';
import { LICENSE_ALLOCATED_STATUS, LICENSE_TYPE } from '../../../constants';
export const createAccountAndAdminUser = async (
datasource: DataSource,
adminExternalId: string,
): Promise<{
accountId: number;
adminId: number;
role: string;
tier: number;
}> => {
const { identifiers: account_idf } = await datasource
.getRepository(Account)
.insert({
tier: 1,
country: 'JP',
delegation_permission: false,
locked: false,
company_name: 'test inc.',
verified: true,
deleted_at: '',
created_by: 'test_runner',
created_at: new Date(),
updated_by: 'updater',
updated_at: new Date(),
});
const account = account_idf.pop() as Account;
const { identifiers: user_idf } = await datasource
.getRepository(User)
.insert({
account_id: account.id,
external_id: adminExternalId,
role: 'admin none',
accepted_terms_version: '1.0',
email_verified: true,
auto_renew: true,
license_alert: true,
notification: true,
encryption: true,
encryption_password: 'password',
prompt: true,
created_by: 'test_runner',
created_at: new Date(),
updated_by: 'updater',
updated_at: new Date(),
});
const user = user_idf.pop() as User;
// Accountの管理者を設定する
await datasource.getRepository(Account).update(
{ id: user.account_id },
{
primary_admin_user_id: user.id,
},
);
return {
accountId: account.id,
adminId: user.id,
role: user.role,
tier: account.tier,
};
};
export const createAccount = async (
datasource: DataSource,
): Promise<{ accountId: number }> => {
@ -116,6 +179,33 @@ export const getLicenses = async (
return licenses;
};
/**
* ユーティリティ: 指定外部IDを持つユーザーを取得する
* @param dataSource
* @param externalId ID
* @returns
*/
export const getUserByExternalId = async (
datasource: DataSource,
externalId: string,
): Promise<User> => {
const user = await datasource.getRepository(User).findOne({
where: {
external_id: externalId,
},
});
return user;
};
/**
* ユーティリティ: すべてのユーザーを取得する
* @param dataSource
* @returns
*/
export const getUsers = async (dataSource: DataSource): Promise<User[]> => {
return await dataSource.getRepository(User).find();
};
/**
*
* @param datasource

File diff suppressed because it is too large Load Diff

View File

@ -45,13 +45,13 @@ export class User {
@Column({ default: true })
notification: boolean;
@Column({ nullable: true })
@Column({ default: false })
encryption?: boolean;
@Column({ nullable: true })
encryption_password?: string;
@Column({ nullable: true })
@Column({ default: false })
prompt?: boolean;
@Column({ nullable: true })