2020/07/06

Jestで時刻を固定化する方法


背景


Node.jsで動作するサーバーアプリのユニットテストコードをJestで作成するため、Jestの使用方法について調査した。

記事の目的


Jestで時刻を固定する

Date関数 / moment関数


ここでは、Date関数 / moment関数について記載する。

Date関数とは

Date関数は、Javascript標準の時刻モジュールである。現在時刻の取得などが行える。

moment関数とは

moment関数は、Date関数のラッパーであり、時刻差の計算などの機能が付与されている。

時刻の固定


時刻固定の必要性

ある関数やクラスのユニットテストを実行する際、堅牢性確認のため、閏年や特定の時刻でバグが出現しないか確認する必要がある。

時刻の固定方法

MockDateモジュールで時刻を固定できる。

/test/date.test.ts
  1. import MockDate from "mockdate";
  2. import moment from "moment";
  3. describe("時刻の固定化", () => {
  4. beforeAll(() => {
  5. // JSTで時刻を固定できる
  6. MockDate.set("2019-08-01 21:00:00");
  7. });
  8. afterAll(() => {
  9. // 時刻の固定を解除する
  10. MockDate.reset();
  11. });
  12. test("Dateは固定時刻の文字列を返す", () => {
  13. const time = new Date();
  14. expect(time.toISOString()).toBe("2019-08-01T12:00:00.000Z");
  15. });
  16. test("momentはDateのラッパーなため、固定時刻を返す", () => {
  17. expect(moment().toISOString()).toBe("2019-08-01T12:00:00.000Z");
  18. });
  19. });

まとめ


  • Jestで時刻を固定する方法について調査、記載した

参考文献




変更履歴


  1. 2020/07/06: 新規作成

2020/07/01

Jestで関数、クラスをMockする方法


背景


Node.jsで動作するサーバーアプリのユニットテストコードをJestで作成するため、Jestの使用方法について調査した。

記事の目的


Jestで関数やクラス全体、クラスのメソッドの一部をMockする

Jest


ここでは、Jestについて記載する。

Jestとは

Jestは、Facebookが開発を進めている、オープンソースのJavaScriptのテストフレームワークである。TypeScriptで記述したものでも利用できる。テストフレームワークであるため、テストを書くために必要な一通りの機能(メソッドのMock機能、コードカバレッジレポート機能等)が提供されている。

Mock


Mockとは

ある関数やクラスのユニットテストを実行する際、それらが依存している外部モジュールの挙動によって実行結果が変化することは不適切である。そこで、外部モジュールを、一定の実行結果がモジュールに差し替える。この差し替えたモジュールをMockと言う。

関数のMock

/src/function.ts
  1. /**
  2. * 入力した数値を文字列に変換する
  3. * @param input 数値
  4. * @returns output 文字列
  5. **/
  6. export async function SampleFanc(input: number): Promise<string> {
  7. return String<input>;
  8. }
  9. /**
  10. * 入力した数値に1を足し、文字列に変換する
  11. * @param input 数値
  12. * @returns output 文字列
  13. **/
  14. export async function SampleFanc2(input: number): Promise<string> {
  15. const out = SampleFanc(input+1);
  16. return out;
  17. }
上記の関数sampleFancをMockし、sampleFanc2をテストする場合、下記のようになる。

/test/function.test.ts
  1. import * as sampleFanc from "../src/function.ts";
  2. describe("SampleFancのモック化テスト", () => {
  3. it("モック化できているか", () => {
  4. // spyOnすることによって、該当関数(SampleFanc)の型がspyInplementationに変化する
  5. // mockReturnValueOnce()によって1度だけ設定値を返す関数にMockできる
  6. // jest.spyOnだけでは、実際の関数(Mockされていない関数)が実行されるので注意
  7. const sampleSpy = jest.spyOn(sampleFanc , "SampleFanc").mockReturnValueOnce("1");
  8. // SampleFanc2の実行、返り値が"2"でなければNG
  9. expect(sampleFanc.sampleFanc2(1)).toBe("2");
  10. // SampleSpyが1回以上呼ばれたかを確認、呼ばれていない場合NG
  11. expect(SampleSpy).toHaveBeenCalled();
  12. });
  13. });
  14. }


クラス全体のMock

/src/class.ts
  1. /**
  2. * 入力した数値を文字列に変換する
  3. * @param input 数値
  4. * @returns output 文字列
  5. **/
  6. export class SampleClass {
  7. private data: string;
  8. /**
  9. * 空文字列をセット
  10. */
  11. constructor() {
  12. this.data= "";
  13. }
  14. /**
  15. * 入力された文字列をキャッシュし、前の文字列を返す
  16. * @param input キャッシュする文字列
  17. * @returns output キャッシュされていた文字列
  18. */
  19. public Func1(input: string): string {
  20. const output = this.data;
  21. this.data = input;
  22. return output;
  23. }
  24. /**
  25. * 入力された数値を文字列に変換してキャッシュし、前の文字列を返す
  26. * @param input キャッシュする数値
  27. * @returns output キャッシュされていた文字列
  28. */
  29. public Func2(input: number): string {
  30. const output = this.Func1(String(input));
  31. return output;
  32. }
  33. }
上記のクラスsampleClassをMockする場合、下記のようになる。

/test/function.test.ts
  1. import * as sampleClass from "../src/class.ts";
  2. // jest.mock()によってクラス全体をモック化できます
  3. jest.mock("../src/class.ts"); // パスを指定
  4. const SampleClassMock = sampleClass.SampleClass as jest.Mock; // TypeScriptでは型変換する必要がある
  5. describe("SampleClass のテスト", () => {
  6. it("クラス全体がモックになっているか確認", () => {
  7. // mockImplementationOnceで実装したいクラスを設定する
  8. SampleClassMock.mockImplementationOnce(() => {
  9. return {
  10. data: "1",
  11. Func1: (): string => {
  12. return "2";
  13. },
  14. Func2: (): string => {
  15. return "3";
  16. },
  17. };
  18. });
  19. const sample = new sampleClass.SampleClass();
  20. // SampleClassMockが1度以上呼び出されたか確認
  21. expect(SampleClassMock).toHaveBeenCalled();
  22. // プライベート変数"name"が"1"であるか確認
  23. expect(sample["data"]).toBe("1");
  24. // メソッドFunc1が"2"を返すか確認
  25. expect(sample.Func1("1")).toBe("2");
  26. // メソッドFunc2が"3"を返すか確認
  27. expect(sample.Func2(1)).toBe("3");
  28. });
  29. });

クラスの一部をMock

/src/class.ts
  1. /**
  2. * 入力した数値を文字列に変換する
  3. * @param input 数値
  4. * @returns output 文字列
  5. **/
  6. export class SampleClass {
  7. private data: string;
  8. /**
  9. * 空文字列をセット
  10. */
  11. constructor() {
  12. this.data= "";
  13. }
  14. /**
  15. * 入力された文字列をキャッシュし、前の文字列を返す
  16. * @param input キャッシュする文字列
  17. * @returns output キャッシュされていた文字列
  18. */
  19. public Func1(input: string): string {
  20. const output = this.data;
  21. this.data = input;
  22. return output;
  23. }
  24. /**
  25. * 入力された数値を文字列に変換してキャッシュし、前の文字列を返す
  26. * @param input キャッシュする数値
  27. * @returns output キャッシュされていた文字列
  28. */
  29. public Func2(input: number): string {
  30. const output = this.Func1(String(input));
  31. return output;
  32. }
  33. }
上記のクラスsampleClassのFunc1のみをMockする場合、下記のようになる。

/test/function.test.ts
  1. import * as sampleClass from "../src/class";
  2. describe("SampleClass のテスト", () => {
  3. it("Func1がモックになっているか確認", () => {
  4. // SampleClass.prototypeのfunc1関数をspyOnすることで、func1関数のモック化ができる
  5. const Func1Spy = jest.spyOn(sampleClass.SampleClass.prototype, "Func1").mockReturnValue("2");
  6. const sample = new sampleClass.SampleClass();
  7. // Func2の実行結果が"2"であるか確認
  8. expect(sample.Func2(1)).toBe("2");
  9. // Func1Spy が1度以上呼び出されたか確認
  10. expect(Func1Spy).toHaveBeenCalled();
  11. });
  12. });

まとめ


  • Jestで関数やクラス全体、クラスのメソッドの一部をMockする方法について調査、記載した

参考文献




変更履歴


  1. 2020/07/01: 新規作成

MQTTの導入

背景 IoTデバイスの接続環境構築のため、MQTT(mosquitto)の導入を行った。 記事の目的 MQTT(mosquitto)をUbuntuに導入する mosquitto ここではmosquittoについて記載する。 MQTT MQTT(Message Qu...