前回のCypressとPercelとElmでATDDにFirebaseをくっつけました。ほとんど変わりはありませんが、外部に状態を持つような場合にはcypressではどのようにするか気になり試してみました。
今回のサンプルコードです。
パフォーマンスを落とさない注意
cypressのサンプルコードから見てみましょう。前回との違いは、beforeEach
を試す場合には、describe
のネストがあると全てのテストケースのときに走らなくなってしまうため、直下はit
にしました。
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を挟んでくれます。つまり一致するまで待ってくれるようになるわけです。
また新たにreset
ボタンを配置しました。これはfirestoreの中身を0で初期化する作用を持っています。実際のサービスではこのようなボタンを設置することはできません。任意のコマンドを実行するcy.exec
と言う命令を使ってみましたが、cliからfirestoreの中身を初期化するのが遅くE2Eとして遅くなりすぎてしまうためresetボタンを設置しました。実務でCIを回すことを考えるとテストケースごとにDBを初期化するのは現実的では無い気がするので、初期化は全てのテストで1回 あとはアカウントを切り替えるなどの工夫が必要そうです。
実装
以前、FirebaseとElmの記事は書いたことがあるため説明を省略します。"Now loading"の部分のみ抜粋してソースコードを載せます。
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側のコードのリファクタリングが安心して行えました。準備や維持が難しい分、強い恩恵が得られるため上手く付き合って行けたらなと思います。