CypressとParcelとElmでATDD

あけましておめでとうございます。年明け一本目の記事になります。
年末に普段の開発をまとめた、[普段のスプリントを言語化してみる
](https://qiita.com/ababup1192/items/be52d6637dfb49950826)という記事も書いたのでよければご覧ください。

今回はタイトルの通りCypressを利用して受け入れテスト駆動開発(Acceptance Test Driven Developmentの、ATDD)をしてみました。Cypressと言うEnd-to-end tests、Integration testsを爆速で便利に回せるフレームワークに入門したかったため、この記事を執筆しました。ブラウザを利用する言語であればどのような言語でも対応可能ですが、ビルドが爆速でATDDを回すのにとても適していたため今回は、Elmを開発言語として使用しました(~~単なる好み~~)。さっそく説明をしていきますが、ローカルではどんな開発風景になるの？ と言うのを伝えるためにGifを置いておきます。あらかじめ感想をお伝えすると、とても体験が良くプロダクトに早く組み込みたいと言う気持ちになりました！！

[![Image from Gyazo](https://i.gyazo.com/64aef3737dd79b36d9cbaab618b94c49.gif)](https://gyazo.com/64aef3737dd79b36d9cbaab618b94c49)

ソースコードは[こちら](https://github.com/ababup1192/elm-cypress-counter)。READMEに従ってやると同じ体験ができます！

## 開発環境

開発環境について説明をしていきます。

依存関係の重要な部分をpackage.jsonから抜き出しました。

```package(一部抜粋).json
"parcel-bundler": "^1.12.4",
"start-server-and-test": "^1.10.6",
"cypress": "^3.8.1"
```

cypressはローカルでのダッシュボードやCIでのテストを動かすためにもちろん必須です。[percel](https://ja.parceljs.org/)は、elmをコンパイルし開発サーバを立ち上げてくれたり、Hot reloadingをしてくれたり何かと便利な存在です。start-server-and-testは、開発サーバが立ち上がるのを待ってからcypressのテストを実行したりしてくれるnodeパッケージになります。

またpackage.jsonの一部を抜粋しますが、今度はnpm scriptsの部分です。重要な部分だけ抜き出しました。

```package(一部抜粋).json
"start": "parcel ./src/index.html --out-dir dist",
"cy:run": "cypress run",
"cy:open": "cypress open",
"dev-start": "start-server-and-test start http://localhost:8000 cy:open",
"test": "start-server-and-test start http://localhost:8000 cy:run"
```

startはparcelを利用して開発サーバを立ち上げます。dev-startを実行すると、以下のようなElectron製のcypressダッシュボードが起動します。ここでは作成したspecが並びます。specファイルを選択すると冒頭のGifのようにChromeが立ち上がりテストを開始します。

<img width="993" alt="スクリーンショット 2020-01-01 16.08.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48687/4e361f47-9ad0-a3cc-4254-bb5c0154cdcb.png">

testはCIで回すようのコマンドです。開発サーバを起動し上手く動いたタイミングで、cypressのテストがCLIで走ります。

<img width="1126" alt="スクリーンショット 2020-01-01 16.11.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48687/59d89fa9-eb63-09af-6b0e-f6ae7c5a3fe5.png">

最後にcircle ci用のyamlファイルです。見たまんまですが、cypressを動かすには専用のDocker imageを使うと良いでしょう。

```.circleci/config.yml
version: 2
jobs:
  build:
    docker:
      - image: cypress/base:8
        environment:
          ## this enables colors in the output
          TERM: xterm
    working_directory: ~/project
    steps:
      - checkout
      - run:
          name: Install Dependencies
          command: npm install
      - run:
          command: npm test
```

## ATDDをしてみる

さっそくATDDをしてみましょう。`npm run dev-start`コマンドで、ダッシュボードを起動して開発開始です。以下のようなファイルを作りましょう。公式の書き方に一旦のっとって書いていますが、最近のESぽく書いたりTypeScriptにも対応しているので、詳しく知りたい方は公式ドキュメントをご覧ください。テストは説明が不要なほどシンプルです。カウンターのテストでは、各テストのはじめに開発URLにアクセス(visit)します。その後、+ボタンの機能のテストでは、+が含まれた属性を探しクリックし、カスタムデータ属性でセットしたcypress用のidからカウンタの値を見て想定した値になっているかテストをします。

```counter_spec.js
describe("Elm Counter Test", function() {
  beforeEach(function() {
    cy.visit("http://localhost:8000/");
  });

  const counterButtonElementId = '[data-cypress-id="counter-num"]';

  describe("+ button", function() {
    it("Increment number", function() {
      cy.contains("+").click();

      cy.get(counterButtonElementId).should("have.text", "1");
    });
  });

  describe("- button", function() {
    it("Decrement number", function() {
      cy.contains("-").click();

      cy.get(counterButtonElementId).should("have.text", "-1");
    });
  });
});
```

今回対象になるプログラムはElmのHelloWorldである[カウンタ](https://guide.elm-lang.jp/architecture/buttons.html)プログラムです。唯一手を入れた部分はidを振るところです。attribute関数を利用して、割り振りましょう。

```Main.elm
view : Model -> Html Msg
view model =
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [ attribute "data-cypress-id" "counter-num" ] [ text (String.fromInt model) ]
    , button [ onClick Increment ] [ text "+" ]
    ]
```

# まとめ

環境構築含め、とてもハードル低くATDDができると感じていただけたのではないでしょうか？ 実務に採用するには、また別な壁がありますが、cypress自体はとても簡単に思えました。これからもっと触って、経験値を貯めていこうと思います。