JestでDateのコンストラクタをモックする

下記の関数があるとしましょう。

1
2
3
function calcDuration(date: string) {
return Math.abs(new Date().getFullYear() - new Date(date).getFullYear());
}

この関数のテストを書くのに、注意しなければいけないことが1点あります。もう気づいたと思いますが、new Date()の結果を固定値にしないと、テストの結果が実行時の日付に依存してしまいますので、環境依存が発生し許容できないでしょう。Jestにはモジュールモックを含め、いくつのやり方がありますが、標準ビルトインオブジェクトかつコンストラクタの場合、どうモックするか、ドキュメントには詳しく書かれませんでした。ウェブで検索してみた結果jest.spyOn(global, 'Date')という感じで書くのは一番直感的で柔軟性が高いと感じました。よって、テストのコードはこうなるかと思います。

1
2
3
4
5
6
test("mock", () => {
const dateToUse = new Date("2015-01-01");
jest.spyOn(global, "Date").mockImplementation(() => dateToUse);
const duration = calcDuration("2011-01-01");
expect(duration).toBe(4);
})

しかし、上記のコードの実行結果は[Jest] Expected value to be (using Object.is): 4, Received: 0というエラーになるでしょう。理由は簡単です。calcDuration関数内でDateコンストラクタは二回も呼ばれ、そして常にdateToUseが返ってきます。よって、差分が0になるのは正しい振る舞いです。そこを修正する為に、mockImplementationにて条件分岐を作ればシンプルに解決できます。引数が空の場合dateToUseを返し、それ以外は元々のビルドインオブジェクトを使わせます。結果は下記の感じです。

1
2
3
4
5
6
7
8
9
10
test("mock", () => {
const OriginalDate = Date;
const dateToUse = new Date("2015-01-01");
const mocked = jest.spyOn(global, "Date").mockImplementation((arg) => {
return arg ? new OriginalDate(arg) : dateToUse;
});
const duration = calcDuration("2011-01-01");
expect(mocked).toHaveBeenCalledTimes(2);
expect(duration).toBe(4);
});

他にmockImplementationOnceでやる方法も考えられますが、実装を意識しないと書けなさそうと思いましたので、上記のやり方を採用することにしました。

Read More

List of Whatever Cases

NameExample
flat casesamplenamingconvention
pascal case / capital camel caseSampleNamingConvention
camel casesampleNamingConvention
kebab case / chain case / spinal case / lisp case / caterpillar casesample-naming-convention
train caseSample-Naming-Convention
snake casesample_naming_convention
screaming snake case / macro caseSAMPLE_NAMING_CONVENTION

References

なんちゃらケース系の用語が多すぎるからまとめてみました。以上

Read More

「Kotlin Fest 2018」参加レポート

Kotlinは趣味でちょろっとしか書いていませんが、Kotlinの哲学である実用主義・簡潔・安全・相互運用性に魅了され、理想と現実のバランスをうまくとっているなと感心しましたので、Kotlin Fest 2018へ参加してきました。とても楽しかったし、勉強にもなった為、レポートとしてまとめたいと思いました。

Kotlinもう一歩

最初に参加したセッションはヤフーの森さんによる「Kotlinもう一歩」でした。Kotlinのタイプシステムを用いてGenericsとType Projectionを紹介していました。プレゼンの内容をよくまとめたのは下記の一枚です。

タイプシステムの観点で不変・共変・反変を説明してくれてとても印象的だと感じました。タイプシステムを一言で説明すれば、「タイプAが期待される全ての箇所で、タイプBが使用可能であれば、タイプBはサブタイプだと言える。逆に、タイプAはスーパータイプである。」になります。この一言さえ分かれば、「タイプAは自分自身のサブタイプであり、スーパータイプでもある」ということは理解できるでしょう(ちなみに、これはisSubtypeOfisSupertypeOfで検証できる)。また、「StringString?のサブタイプ」というのもわかるはずです。

How to Test Server-side Kotlin

実例を交えた説明でわかりやすかったです。特に技術選定時に考えたこととハマったポイントの紹介はとても参考になりました。テストコードを書きやすくする為に作られたFactlinkotlin-fill-classは確かに使いやすそうだなと感じました。

全体のイメージとして、Kotlin製のテストライブラリはまだまだ少ないと感じました。ただ、Javaの資産は基本的にそのまま使えるので、それほど困らないです。クラスがFinal、Immutable、Null許容しないなどの特性によってハックしなければいけないケースがあるものの、その場合はOSSで提供されているラッパー(i.e. mockito-kotlin)かプラグイン(i.e. all-open)、もしくはコード上の設定(i.e. @JvmStatic)を使えば大体回避できます。

Kotlin Linter

KotlinのLinterについて、一番主流のktlintしか知りませんでした。実際にktlint以外、detekt(detectの意味?w)とアンドロイド用のandroid-lintが存在します。その比較は下記のスライドにてまとめられています。

ktlintと比べてdetektは標準ルールがたくさんありますし、個別の設定ができるし、メッセージ表示も詳しいです。さらに、detekt-formattingを使えばktlintのルールすらサポートできてしまいます(formattingと言いつつも、自動フォーマットと無関係)。整形できないのは状況によって痛いかもしれませんが、選択肢として考えられますね。
また、ktlintのカスタムルールの作成方法は以下のようになります。

Read More

Vuex Plugin 101

Vue.js Tokyo v-meetup #7」にて発表させて頂きました。
スライドはこちらです。

発表したこと

  • Vuexのプラグインは簡単に作れますよ。
  • VuexのプラグインはよくsubscribeというストアインスタンスのAPIを使ってます。
  • ただし、subscribeのコールバックはmutate後呼ばれますので、もっと早い段階で処理を加えたければ、2.5.0から導入されたAPI(subscribeAction)を使いましょう。
  • Vuexのプラグインを探したければ、awesome-vueリポジトリはいいところです。

尺の関係で話せなかったこと(スライドに入れました)

  • 名前通りあくまでsubscribeなんで、いわゆるAOPのような使い方は期待しないでください。
  • vuex-shared-mutationsというタブ間の状態を同期してくれる素晴らしいVuexプラグインがあるが、実は考え方はとてもシンプルです。
  • 他にも色んな有用なプラグインがありますよ。

Read More

ある条件を満たすDOM要素を抽出するコード

週末にデモプログラムを作成する時、ある要素の中から、指定された条件を満たすDOM要素を抽出する機能が必要だった為、下記のようなコードを作りました。

1
2
3
4
5
const selectAll = el => condition => [el,
...Array.from(el.children)
.map(child => selectAll(child)(condition))
.reduce((arr, i) => arr.concat(i), [])
].filter(condition)

考え方

実は、主な作業は2つだけです。

  1. 全ての要素を羅列します。
  2. 条件に満たしている要素をフィルターリングで抽出します。

コードを書けばこうなります。(allElements=全ての要素、condition=条件)

1
const selectAll = (allElements, condition) => allElements.filter(condition)

関数の再利用を考えたら、コンポジション)を採用した方がいいでしょう。

1
const selectAll = allElements => condition => allElements.filter(condition)

1つ目の作業はどう実現するかは問題です。DOMはツリー構造なので、親要素の元に要素があり、その要素の中に、さらに子要素が存在する。ツリー構造のケースに対処する場合、再帰を使用することは一般的です。関数型風に書きたかったので、Array.prototye.mapとスプレッド構文を利用することにしました。DOM要素のchildrenはHTMLCollection型で、map関数が備えていません。よって、まずArray.from()で配列に変換する必要があります。

Read More

2017年Product Huntで紹介されたプロダクト12選

Product Huntより公開された電子ブック「The Best Product Launches of 2017」を読み終えました。全体的に人工知能系と仮想通貨系(特に下半期)のモノが多かったと感じています。ここで、個人的に気になった12つのプロダクトを紹介したいと思います。(読む前にすでに使用している・知っているプロダクトは含まれていません。)

12. The Startup Way

Cover of The Startup Way

ひとことコメント:ベストセーラー「The Lean Startup」の作者の新作です。

11. Product Frameworks

Product Frameworks introduces Basecamp Product Cycles

ひとことコメント:(プログラミングではなく)プロダクト開発でよく使われているフレームワークをまとめてくれたサイトです。

10. Intercom on Starting up

Intercom on Starting up

ひとことコメント:Intercomから出したスタートアップに関する書籍で、GoodReadsで4.2の高評価をマークしています。

Read More

オブジェクトにあるプロパティが存在するかをチェックする

手法

JSのオブジェクトを扱う際に、あるプロパティが存在するかをチェックする作業はしばしば発生します。実は、この一見簡単そうな作業を実施する為に、いくつかのやり方が存在します。考え方で分けて見れば、以下の2種類になるかと思います。

該当プロパティの値をチェックする

よく見かける手法はundefined !== obj[prop]もしくはundefined !== obj.propでしょう(obj = オブジェクト、prop = プロパティ)。まずオブジェクトからプロパティを出して値をみます。もしオブジェクトに該当プロパティが存在しなければ、値はundefinedになりますので、この特性を利用する手法ですね。
この手法の異種として、void(0) !== obj[prop]があります。void演算子が分かればすぐ理解できるでしょう。void(0)の戻り値がundefinedになるからですね。
また、'undefined' !== typeof obj[prop]という書き方もあります。値の型を取得し、undefined型であるかを判別する考えです。
更に、もっとお洒落な書き方も存在します。!!obj[prop]です。下記のように書くことが可能になります。

1
if(!!obj.prop) return 'OK';

エクスクラメーションマークを2つ先頭に追加することで、結果の反転を二回行います。「False→True→False」みたいに、元の結果に戻すやり方ですね。

undefined !== obj[prop]がもっと直感的なのに、なぜわざわざvoid(0) !== obj[prop]'undefined' !== typeof obj[prop]を使うか、疑問を感じたので、ベンチマックを行ってみました。実は後二者の効率は前者の3倍程になります。なるほど〜
あともう一つ気を付けなければいけないのは元々プロパティの値はundefinedになっているケースです。上記の手法を使ってしまったら、該当プロパティが存在していても、falseが出てしまいます。
特に!!obj[prop]を使用する時、もっと注意を払わなければなりません。0nullの反転結果はtrueになるから、該当プロパティに入る値の型を100%把握していなければ、使用するのをやめておいた方が安全でしょう。

該当プロパティの存在を確認する

抽出を行わず、直接チェックする方法もあります。inもしくはhasOwnPropertyの使用です。

1
2
3
4
5
// in
if(prop in obj) return 'OK';

// Object.prototype.hasOwnProperty
if(obj.hasOwnProperty(prop)) return 'OK';

Read More