LoginSignup
5
5

More than 3 years have passed since last update.

CypressとPercelとFirebaseとElmでATDD

Posted at

前回のCypressとPercelとElmでATDDにFirebaseをくっつけました。ほとんど変わりはありませんが、外部に状態を持つような場合にはcypressではどのようにするか気になり試してみました。

今回のサンプルコードです。

パフォーマンスを落とさない注意

cypressのサンプルコードから見てみましょう。前回との違いは、beforeEachを試す場合には、describeのネストがあると全てのテストケースのときに走らなくなってしまうため、直下はitにしました。

counter_spec.js
describe("Elm Counter Test", function() {
  const counterButtonElementId = '[data-cypress-id="counter-num"]';

  beforeEach(function() {
    cy.visit("http://localhost:1234/");

    cy.get(counterButtonElementId).should("not.have.text", "Now loading");
    cy.contains("reset").click();
  });

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

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

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

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

visitは前と変わりませんが、counterの値が"Now loading"ではないことをテストしています。Seleniumに慣れている場合、wait, sleepのようなものを使ってしまいがちですが(実際に最初にそうしてしまいました)、cypressのassertは即時実行ではなく値が一致しない場合には自動的にwaitを挟んでくれます。つまり一致するまで待ってくれるようになるわけです。

スクリーンショット 2020-01-01 21.55.14.png

また新たにresetボタンを配置しました。これはfirestoreの中身を0で初期化する作用を持っています。実際のサービスではこのようなボタンを設置することはできません。任意のコマンドを実行するcy.execと言う命令を使ってみましたが、cliからfirestoreの中身を初期化するのが遅くE2Eとして遅くなりすぎてしまうためresetボタンを設置しました。実務でCIを回すことを考えるとテストケースごとにDBを初期化するのは現実的では無い気がするので、初期化は全てのテストで1回 あとはアカウントを切り替えるなどの工夫が必要そうです。

実装

以前、FirebaseとElmの記事は書いたことがあるため説明を省略します。"Now loading"の部分のみ抜粋してソースコードを載せます。

Main.elm
type alias Model =
    Maybe Int


init : () -> ( Model, Cmd Msg )
init _ =
    ( Nothing, Cmd.none )

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Increment ->
            case model of
                Just value ->
                    ( Just <| value + 1, setValue <| value + 1 )

                Nothing ->
                    ( model, Cmd.none )

        Decrement ->
            case model of
                Just value ->
                    ( Just <| value - 1, setValue <| value - 1 )

                Nothing ->
                    ( model, Cmd.none )


view : Model -> Html Msg
view model =
    div []
        [ button [ onClick Decrement ] [ text "-" ]
        , div [ attribute "data-cypress-id" "counter-num" ]
            [ text
                (case model of
                    Just value ->
                        String.fromInt value

                    Nothing ->
                        "Now loading"
                )
            ]
        , button [ onClick Increment ] [ text "+" ]
        , div [] []
        , button [ onClick Reset ] [ text "reset" ]
        ]

updateのIncrementやDecrementは、Modelがあるかないかで分岐をし、ある場合のみ表示やFirestoreの更新をしています。

まとめ

Firebaseを使って簡単に状態を持つアプリケーションのATDDをしてみましたが、やはり気にすることと言う意味での難易度はグッと上がります。特にE2Eは実行が遅くなりすぎてしまうことが問題として挙げられるため、なるべくパフォーマンスが出るようなテストを心がけたいと思います。しかし、E2Eの効果は絶大で普段だったら中々できないFirebaseを操作するJavaScript側のコードのリファクタリングが安心して行えました。準備や維持が難しい分、強い恩恵が得られるため上手く付き合って行けたらなと思います。

5
5
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
5
5