RailsでJestを試験的に導入してみた

今回Rails+Docker環境にてJestを試験的に導入してみたので、その過程をまとめていきたいと思います。ちなみにJestというのはJavascriptのテストフレームワークで、以下のリンクにある通りトレンドの上位に位置しています。
[JavaScriptテストフレームワークのトレンド比較](https://qiita.com/yudwig/items/b85c27ac98bd9308e8da)
Jestを導入してみようと思った背景として、バックエンド側では自動テストを導入しており、修正等が入った際には自動テストを回せばエラーを検知できるのですが、フロント側には自動テストはなく手動テストに頼ってしまっているので、修正等が入った際にはいちいち手動テストをやらなくてはいけず手間がかかり、エラーも検知しにくいという状態になっていると感じたからです。

### Jestをインストールしてみる
前提として私のDockerに関する知識が不足している+今回は試験的に自分のDocker環境だけで動くようにするという点を踏まえて見て頂ければと思います。既存のDocker環境にはnode.js・npmの環境が出来上がっていましたので、Dockerに入り下記コマンドでjestをインストールします。今回はjqueryも使用しますので、jqueryもインストールしておきます。

```
npm install --save-dev jest
npm install --save jquery
```

尚、node.js・npmの環境が出来ていなければJestをインストールすることはできませんので、まだの方は先にそれらの環境を構築してください。インストールしたらpackage.jsonに下記項目を足します。(※package.jsonが見当たらないという方はnpm initコマンドを使用して作成するようにしてください)
```javascript:package.json
 "scripts": {
    "test": "jest",
  }
```
これで一旦は環境構築は完了です。それでは実際にテストコードを書いていきたいと思います。

### 実際にJestを使用してテストコードを書いてみる
今回はeventを通じて、DOM要素が書き変わることをテストしようと思います。まずはテスト実行時に読み込む用のDOM要素が記載されたファイルを用意します。
```javascript:dom.js
 // ------------------------------------------
 // 【text_target】textが書き変わることを検証
 // 【hidden_target】要素が非表示になる(display:none)ことを検証
 // 【add_target】クラスが追加されたことを検証
 // 【remove_target】クラスが削除されたことを検証
 // ------------------------------------------
 const dom = `
  <div id="text_target">Text Target</div>
  <div id="hidden_target">Hidden Target</div>
  <div id="add_target">Add Target</div>
  <div class="remove-target" id="remove_target">Remove Target</div>
`
module.exports = dom;
```
それぞれのDOM要素に関する役割については、コメントに記載の通りです。module.exports = dom と記載することによって、外部ファイルから変数に格納されているDOM要素に干渉することができるようになります。続いて実際にテストファイルを書いていきます。
```javascript:dom.test.js
// テスト対象のコードファイルを取り込む
const Dom = require('./dom')
var $ = require('jquery');

function changeDom() {
    $("#text_target").text("Change Text")
    $("#hidden_target").hide()
    $("#add_target").addClass(".add-target")
    $("#remove_target").removeClass(".remove-target")
}
test('change dom', () => {
    document.body.innerHTML = Dom;
    changeDom()
    expect($('#text_target').text()).toEqual('Change Text');
    expect($('#hidden_target').css('display')).toEqual("none");
    expect($('#add_target').hasClass(".add-target")).toEqual(true);
    expect($('#remove_target').hasClass(".remove-target")).toEqual(false);
})
```
ファイルの一番上で用意していたdomファイルを読み込んでいます。そして関数を1つ用意してやり、testコードの中で関数を呼び出してDOM要素を書き換え、それぞれexpectの中で取れた値と等しいかをチェックしています。それでは実際にテストを実行してみます。Dockerに入り、下記コマンドを流します。
```
npm test
```
成功すれば下記のような表示が出てくるはずです。思っていたより簡単にテストが書けて実行できましたね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576652/fc63dbaf-d2c0-922a-3a75-e6d60aa729b0.png)

### (補足) Node.jsのバージョンに気をつけよう
npm testコマンドを実行するにあたり、下記エラーに悩まされました。
```
globalThis is not defined
```
調べてみたところNode.jsのバージョンによってJestが上手く動作しないことがあるようです。Node.jsはバージョン指定をせずインストールしようとすると安定版がインストールされるようで、v.10.24となっていました。そこでバージョン指定をしてインストールするようにしてみました。
```
curl -sL https://deb.nodesource.com/setup_16.x | bash -
apt update
apt install -y nodejs
```
上記3つのコマンドを使用して、アップデートしたところ無事にnpm testが通るようになりました。同じエラーに悩まされた際には参考にして頂ければと思います。

## 今後の課題
今回は簡単な例を元にテストを書いてみましたが、実際に使用しているJsファイルをどうテストに組み込んでいくかはしっかり考えていかなければならないと感じました。今回の例のように単発的なものであれば簡単に書くことができますが、連鎖的に動いていくロジックの中でどの点に焦点を当ててテストをしていくのか考えなければいけないことは多そうです。これは個人的な余談ですが、Jestの導入にあたり改めてDockerの勉強もしなければいけないなと痛感しました。今回は試験的ということで、自分のDocker環境だけで動くようにしましたが、本格的にチームで導入する場合にはDockerfile内にインストールする内容を書いていかなければならなかったり、新しくコンテナを立ち上げるといったことも考えられるので、まだまだやることはたくさんありそうです。