2018-8-9
DIに関する覚書
ユニットテストしやすくするために、クラス内で他クラスのインスタンスを生成するのでなく、コンストラクタの引数としてインスタンスを受け取ろうぜっていうアレ
例
実際に VRChat の API を叩くサンプルプロジェクトを作って試してみた。
DI なし
import { VrcApi } from './vrcapi';
import { config } from './data/config';
export class NotDI {
private _api: VrcApi;
constructor() {
this._api = new VrcApi(
config.username,
config.password
);
}
async fetchUserId(username: string) {
await this._api.init();
const userData = await this._api.fetchUserByName(username);
return userData.id;
}
}
DI 無しの場合はこのようにコンストラクタ内で VRChat の API を扱うためのクラスVrcApi
をインスタンス化することになる。
この場合、ユニットテストの際も常に VRChatAPI 本番環境に直接通信を行うようになってしまう。(一応フラグとかを設定して、フラグによってモックにつなぎにいくみたいな形にすることは可能なのかな?)
DI 有り
import { AbstractVrcApi } from './vrcapi';
export class DI {
constructor(private _api: AbstractVrcApi) {}
async fetchUserId(username: string) {
await this._api.init();
const userData = await this._api.fetchUserByName(username);
return userData.id;
}
}
DI 有りの場合、上記のように抽象クラスであるAbstractVrcApi
のインスタンスをコンストラクタの引数として受け取るようにしている。
こうしておくと、
import { VrcApiMock } from '../src/vrcapi';
import { DI } from '../src/di';
describe('NotDI', () => {
test('Get User ID', () => {
const api = new VrcApiMock();
const notDI = new DI(api);
return notDI.fetchUserId('nekomasu')
.then((id) => {
expect(id).toBe('usr_d0d44753-45d6-4c8e-b7de-ba9c53cb31a1');
});
});
});
このように、ユニットテストする際にモッククラスであるVrcApiMock
のインスタンスを渡すことで、ユニットテスト時にモックサーバー等への通信によるテストを行うことが出来る。
その他
Jest でテスト時にCross origin null forbidden
というエラー
jsdom により、ブラウザ環境をシミュレートしてテストが実行されているため、クロスオリジン通信をしようとしているとエラーが出てしまう。
node 環境の場合は、jest.config.js
でtestEnvironment
にnode
を設定すれば問題ない。
VRChat の username と displayName は異なる
普段表示されているのは displayName、大文字で登録したものは小文字として username に登録されるっぽい。
displayName: Nekomasu
username: nekomasu