You have a bug in your view that only shows up sometimes, under as-yet-undetermined conditions, but the problem is that this view is part of a specific workflow in the application, so you have to get to the right page, fill out a form and click through a couple of dialogs before you can even see if the view is displaying correctly. If not, it’s back to tweaking the code to introduce a potential fix, then reload the application, and start over to get to the view again. Does this sound familiar?
Like any other code, views can have bugs. However, they’re often tested manually, which means that you have to keep going through the same steps over and over to test changes. Tedious.
Fortunately, as of version 1.2.0,
elm-explorations/test includes a few new modules which allow us to test view code and hopefully avoid the need for some of the laborious manual testing. (These modules have been migrated from a separate package,
eeue56/elm-html-test, by Aaron VonderHaar - thanks Aaron!)
Let’s look at a couple of simple examples. Suppose that we have a login form view written like this:
import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) type Msg = Login viewLogin : Html Msg viewLogin = div [ class "login" ] [ input [ placeholder "Organisation", value "" ]  , input [ placeholder "User name", value "" ]  , input [ placeholder "Password" , type_ "password" , value "" ]  , button [ onClick Login ] [ Html.text "Login" ] ]
We can test that this function correctly generates the login button in this manner:
import Test.Html.Query as Query import Test.Html.Selector as Selector -- ... test "Login form has a Login button" <| \() -> viewLogin |> Query.fromHtml |> Query.find [ Selector.tag "button" ] |> Query.has [ Selector.text "Login" ]
This is just a regular test that you would include in your test suite that’s exercised by
First, we have to transform the
Html value using
Query.fromHtml. Then, we find the relevant element using
Query.find, and finally check that its text is “Login”. The
Query module provides a number of other functions.
Another test could ensure that the password field has the
type attribute set to “password” (so that it doesn’t accidentally get replaced with a text field):
test "Password field is of the right type" <| \() -> viewLogin |> Query.fromHtml |> Query.find [ Selector.attribute <| type_ "password" ] |> Query.has [ Selector.tag "input" ]
We can also test that clicking the login button produces the
Login message (after importing
import Test.Html.Event as Event import Test.Html.Query as Query import Test.Html.Selector as Selector test "Login form generates the message" <| \() -> viewLogin |> Query.fromHtml |> Query.find [ tag "button" ] |> Event.simulate Event.click |> Event.expect Login
In this case, we have to find the button first (with
Query.find), then simulate a click event on it with
Event.simulate Event.click, and then verify that it results in the
Login message. The
Event module provides functions for simulating a number of other interactions.
In my book, Practical Elm, I talk a lot more about testing. You can learn how to write unit tests and property/fuzz tests with
elm-test, and how to test your
update function with random lists of messages. Actually, there’s a whole chapter on preventing and finding bugs, so for example you can learn how to leverage the type system to that end and how to use the time-travelling Elm debugger. Check it out!
My book, Practical Elm for a Busy Developer, skips the basics and gets right into explaining how to do practical stuff. Things like building out the UI, communicating with servers, parsing JSON, structuring the application as it grows, testing, and so on. No handholding — the focus is on giving you more substance.
It’s up to date with Elm 0.19.
Pop in your email to get a sample chapter.
(You will also get notifications of new posts along with other mailing list only freebies.)