0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

jestとかjasmineでmomentを含むオブジェクトの曖昧なマッチャーを書く

Posted at

バージョン情報

  • "jasmine-core": "~3.4.0"
  • "karma": "~4.1.0"
  • "jest": "^24.9.0"

(フロントエンドはangular、バックエンドは普通のtypescriptで開発しているため、フロントエンドのテストはkarma/jasmine、バックエンドのテストはjestで行っている構成)

やりたいこと

  • 日付を返すUtilとかの単体テスト

import * as moment from 'moment';

export namespace DateUtil {
  export const nextDay(m:moment.Moment) => {
    return moment(m).add(1,'days');
  }
}

例えばこういうUtil(本当はもっとやる気があるが)があったとして、こういうテストを書きたいとする。

import { DateUtil } from './date-util.ts';

describe(`DateUtil#nextDay`,()=>{
  it(`should return the next day when passed 大晦日`,()=>{
    expect(DateUtil.nextDay(moment('2018-12-31','YYYY-MM-DD')).toEqual(moment('2019-01-01','YYYY-MM-DD'));
  });
});

問題

普通にテストすると、こんな感じで怒られが発生する。
エラーメッセージはkarma/jasmineの場合だが、jestでも似たような状況になる

Chrome 77.0.3865 (Windows 10.0.0) DateUtil#nextDay should return the next day when passed 大晦日 FAILED
        Error: Expected $._i = '2018-12-31' to equal '2019-01-01'.
        Expected $._pf.parsedDateParts[0] = 2018 to equal 2019.
        Expected $._pf.parsedDateParts[1] = 11 to equal 0.
        Expected $._pf.parsedDateParts[2] = 31 to equal 1.
        Expected $._a[0] = 2018 to equal 2019.
        Expected $._a[1] = 11 to equal 0.
        Expected $._a[2] = 31 to equal 1.

まあ、言いたいことはわかる。
インスタンス化の過程の情報を持っているようなイメージだろうか。

ちなみにこれはうまくいく。

// 同様の経路でインスタンス化されたmoment.Momentのインスタンスの比較
expect(moment('2019-01-01','YYYY-MM-DD')).toEqual(moment('2019-01-01','YYYY-MM-DD'));
// 違う経路でインスタンス化されたmoment.MomentのISOString自体の比較
expect(DateUtil.nextDay(moment('2018-12-31','YYYY-MM-DD').toISOString()).toEqual(moment('2019-01-01','YYYY-MM-DD').toISOString());

つらさ

toISOString()とかvalueOf()して比較するというのも手だが、これが構造化されたデータだったりするとつらい。
なるべくなら、jasmineやjestの機能にうまく乗る形で表現したい。

解決案

jasmineにはAsymmetric Matcherというものがある。
有名なのはjesmine.objectContainingとかで、要するに「厳密な二つのオブジェクトの完全一致ではなく、特定の条件を満たした場合にマッチ成功とみなす」ことを可能にしてくれる。
今回はこれを自作することにする。
参考:https://jasmine.github.io/2.2/introduction#section-Custom_asymmetric_equality_tester


export class MomentContaining {
  constructor(private sample:moment.Moment){}
  asymmetricMatch(other:any){
    // ここらへんは適当。ほかに比較したいフィールドや振る舞いがあれば追加
    if(!other.isValid || !other.isValid() || !other.toISOString)){
      return false;
    }
    return other.toISOString() === this.sample.toISOString();
  }
}
export const momentContaining = (sample:moment.Moment) => new MomentContaining(sample);

ちなみにjestのソースをあたると以下のように書いてある。
https://github.com/facebook/jest/blob/master/packages/expect/src/asymmetricMatchers.ts
呼び出されている箇所は以下の通り。
https://github.com/facebook/jest/blob/master/packages/expect/src/jasmineUtils.ts#L42

要はasymmetricMatchという関数を持っているオブジェクトが比較する二つのもののうちどちらかにあった場合、それを呼び出した結果のbooleanをマッチ結果としてみなすという構造になっている。

これでやってみると通るようになる。
最終的なコードは以下の通り。

import { DateUtil } from './date-util.ts';


export class MomentContaining {
  constructor(private sample:moment.Moment){}
  asymmetricMatch(other:any){
    if(!other.isValid || !other.isValid() || !other.toISOString)){
      return false;
    }
    return other.toISOString() === this.sample.toISOString();
  }
}
export const momentContaining = (sample:moment.Moment) => new MomentContaining(sample);

describe(`DateUtil#nextDay`,()=>{
  it(`should return the next day when passed 大晦日`,()=>{
    expect(DateUtil.nextDay(moment('2018-12-31','YYYY-MM-DD')).toEqual(momentContaining(moment('2019-01-01','YYYY-MM-DD')));
  });
});

当然だが、momentContainingに渡す日付を変えてみるときちんと失敗してくれる。

ちなみに、失敗したときはメッセージにMomentContainingのtoString()したものが表示されるっぽい気がする。

Chrome 77.0.3865 (Windows 10.0.0) DateUtil#nextDay should return the next day when passed 大晦日 FAILED
        Error: Expected Tue Jan 01 2019 00:00:00 GMT+0900 to equal MomentContaining({ sample: Wed Jan 02 2019 00:00:00 GMT+0900 }).

なのでtoStringを実装するときれいなメッセージになる。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?