この記事は Angular Advent Calendar 2018 の 18日目の記事です。
今回は最近NgRxの新機能として追加されたMockStoreについて書きたいと思います。
NgRxの基本的な使い方については14日目でkiita312さんが書かれていますので、ぜひそちらをご参考ください。
Ngrxをこれから始める人への入門ガイド
MockStoreとは
MockStoreとは、Storeを使ったComponentでのテストを簡単に行えるようにする為のモジュールです。
10月末にmasterにマージされ、v7.0.0でリリース予定となりました。
まだドキュメントが作成されておらずngrx.ioにも記載がないのですが、機能自体はstableとなっています。
非常に便利なので、v7(beta)を使用している方はこの記事を参考にぜひ使い始めてみてください。
残念ながらv7は現在(2018-12-18時点)まだstableとしてリリースされていないため、6系を使用している方はもう少しだけ待つ必要があります。
MockStoreの目的
MockStoreはStoreを使用しているComponentのテストをシンプルに書けるようにするために作られています。
NgRxではComponentはStoreに対してselect/dispatchを行うだけの関係で、StoreやReducerの実装を知る必要がありません。これらは完全な疎結合な関係になっています。しかし、実際にテストを書く際は通常のモジュール設定を行うのと同様にStoreとReducerを設定する必要があります。つまりComponentのテストを書く為には必要なStoreとReducerの関係を理解して一つ一つ設定する必要があります。これは本来不要な作業で面倒なだけです。
MockStoreを使用すればこれらの設定をすることなくStoreを使用することができます。
例として、MockStoreを使用しない場合とした場合のComponentのテスト設定を比較してみましょう。
MockStoreを使わないComponentのテスト設定
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({
users: combineReducers(userReducers),
}),
],
})
Componentのテスト準備としてStoreModuleの設定と関連するReducerの設定が必要になります。
MockStoreを使ったComponentのテスト設定
TestBed.configureTestingModule({
providers: provideMockStore(),
});
これだけです。StoreやReducerの設定がなくなり非常にシンプルになります。
MockStoreの使い方
では実際にMockStoreの使用方法を見ていきましょう。
基本的にStateを設定するだけのモジュールなので、初期値設定と値変更の2種類の機能しかありません。
Importの設定
MockStoreは @ngrx/store/testing
のモジュールから提供されています。v7を使用している場合は追加でインストールすることなくすぐに利用できます。
import { MockStore, provideMockStore } from '@ngrx/store/testing';
Stateの初期値の設定
MockStoreは先程の provideMockStore
に初期値となるStateを渡すことが出来ます。指定しない場合は undefined
が初期値として使われます。
interface MockStoreConfig<T> {
initialState?: T;
}
function provideMockStore<T = any>(config: MockStoreConfig<T> = {}): Provider[];
実際に以下のように使用することができます。
describe('Stateの初期値の設定', () => {
let mockStore: MockStore<any>;
const initialState = {
users: [{ id: 1, name: 'user1' }, { id: 2, name: 'user2' }],
};
beforeEach(() => {
TestBed.configureTestingModule({
providers: provideMockStore({ initialState }),
});
mockStore = TestBed.get(Store);
});
it('Storeの初期値はinitialStateで設定されている', (done: any) => {
mockStore.subscribe(val => {
expect(val).toEqual(initialState);
done();
});
});
});
この記事ではComponentのテストのサンプルコードは割愛しますが、OnInit内で store.select()
をしている場合はこの初期設定を使用すると良いでしょう。
Stateの値の変更
MockStoreではReducerを使用しないので、Stateの値の更新方法が変わります。Stateの値を変更したい時はActionをdispatchするのではなく MockStore.setState
というメソッドを使用して、任意のタイミングで手動でStateを設定します。
export class MockStore<T> extends Store<T> {
setState(nextState: T): void;
}
実際に以下のように使用することができます。
describe('Stateの値の変更', () => {
let mockStore: MockStore<any>;
beforeEach(() => {
TestBed.configureTestingModule({
providers: provideMockStore(),
});
mockStore = TestBed.get(Store);
});
it('Stateの値はsetStateを使用していつでも変更することが出来る', (done: any) => {
const newState = {
users: [{ id: 3, name: 'user3' }, { id: 4, name: 'user4' }],
};
mockStore.setState(newState);
mockStore.subscribe(val => {
expect(val).toEqual(newState);
done();
});
});
});
なかなか直感的に使用することが出来ます。
Stateの状態ごとにComponentをテストする際などだいぶ見通し良く書くことが出来るようになるかと思います。
おわりに
以上がMockStoreの機能になります。
この記事では概要までの紹介として、実際にComponentのテストの書き方などは別の記事で書ければと思います。
余談ですが provideMockStore
のような機能がEffectsにも存在していて、 provideMockActions
という関数を使うことでEffectsのテストを簡単に書けたりします。
NgRxは非常にテストが書きやすい設計になっていて個人的にとても良いライブラリだと思います。MockStore、ぜひこれから使用していってください。
明日19日目の記事はaoshiさんです!