要約
Browser.Navigation.Keyは自分で生成できなくてテストなどで困るので以下のような型を作ってます
type WithKey m
= WithKey Key m
以上です。ありがとうございました
問題提起
Elm0.19になって起動Program周りが整理されてBrowser packageに集約されました
この中のBrouwser.applicationではルーティングを行う際、Keyが必要になります
このKey型の値はinit関数に渡されるのでそこでよしなに自分のModelに含めることになります
type alias Model = { key : Browser.Navigation.Key, ... }
init : () -> Url -> Key -> ( Model, Cmd Msg )
init _ url key =
( { key = key
, ...
}
, Cmd.none
)
さて、何も問題はありませんね
Modelを引数にとる関数のテストをしたい
あなたの手元にはModelを引数に取る関数があり、これをあなたはテストしたいはずです
updateの中身を切り出した関数とか、Page用のviewとかですね
update msg model =
case msg of
ComplicatedMsg data ->
complicatedUpdate data model
...
view model =
case route of
ListRoute ->
listView model
...
わたしの場合はviewでKeyが問題になりました(コード)。わたしは自分で作ったBibliopolaというStorybook的なのでviewを見ながら開発してるのでModelが生成できないと使えなくなって困るんです
解決策1
問題を解決するのは難しいことではありません
引数にKeyが入ってるとはいえ別に使ってるわけではないので適切に引数の型を狭めればいいです
type alias Model =
{ key : Key
, ham : Ham
, egg : Egg
}
-- view : Model -> Html Msg
view : { a | ham :Ham, egg : Egg } -> Html Msg
view model =
...
こんな感じ。構造的部分型とかいうのかな、まあ詳しくないので呼び方はなんでもいいのですがこれでこのview関数をテストするのにKeyは不要になりました
ついでにaliasしときましょう
type alias SubModel a =
{ a | ham : Ham, egg : Egg }
view : SubModel a -> Html Msg
view model =
...
よしこれでOkですね🎉
解決策2
SubModel aの型変数がうざい
解決策3も出てきますが型いじってるだけで大差ないのでお好きなものがあれば採用ください
アプリケーションの都合で型変数を持たされてしまった型を引数に取るのはなんかうざいのでちょっと逆転させます
type alias Model =
{ ham : Ham, egg : Egg }
type alias WithKey m =
{ m | key : Key }
view : Model -> Html Msg
view = ...
main : Program () (WithKey Model) Msg
main = ...
よしよしこれでProgramの都合はBrowser.applicationが直接依存する関数だけにとどまるぞ🎉
解決策3
Modelの詰め替えがつらい
WithKey Model -> Modelの処理は自動でやってくれないので中身を取り出して詰め替えしないだめです。Modelにフィールド増えたときも増やさないといけないです
まあめんどいです
とうことで今日仕事中に唐突に思いついたんですけどこれでどうでしょうか?
type WithKey m
= WithKey Key m
これなら要素の詰め替えもしなくてよい
解決策2と比べて代わりにupdateの返り値を必ずWithKeyで包まなくてはならなくなったのですが、updateの内で対処するのは理にかなってるので今はこれでやってみてます
おわり
[feldmanがこの件に関して対処するようです]
(https://github.com/elm-explorations/test/issues/24#issuecomment-444143745)
Cmdのテストとかできるようになるんですかねー、たのしみー