elm christmas

Testing in Elm

←Previous postNext post →

A 4 min read written by
Joakim Lindquister

As we have seen in the previous articles, Elm has a pretty awesome type system. Although the compiler is guaranteeing no runtime exceptions, there's a limit to what the compiler can do. We still have to program the correct behavior of our programs. Writing tests allows us to build confidence that our program behaves as we want. We can also practice test-driven-development(TDD), where we are not allowed to write any production code before we have written a failed test. TDD tends to generate more structured and cleaner code.

In today's article, we will look into how we can write unit tests in Elm. We look into the elm-test package and get an understanding of the core concept of the test function.

elm-test

The starting point for testing Elm is the package elm-test. It provides common unit-testing features such as expect, suites, and also the more exciting concept of fuzz-testing. Getting started is as easy as installing the package and running elm-test init. This command initializes a testing directory and a sample test.

File structure

So. How does it work? Let's get started by the core concept, the test function. We use it like this:

import Expect
import Test exposing (..)

suite =
    test "8 * 3 equals 24"
        (\_ -> Expect.equal 24 (8 * 3))

The type signature for the test function looks like this:

test : String -> (() -> Expectation) -> Test

It takes two arguments — first, a string that describes the test — secondly, a lambda function. The lambda function must return an Expectation, which can either be pass or fail. Pretty simple, ey?

Expect

As we saw, the second argument must be functions that return an Expectation. The expect module contains a bunch of useful functions that we can use to assert that our functions return the desired results. A few examples are listed here:

test "Christmas is coming"
       (\_ -> Expect.true "24 is 24" (24 == 24))
test "0 is less than 24"
        (\_ -> Expect.lessThan 0 24)

Grouping tests

When we have multiple tests, it make sense to group them. We can achieve that with the describe function.

describe "List"
    [ describe "Length"
        [ test "Empty list"
            (\_ -> Expect.equal 0 (List.length []))
        , test "With two elements"
            (\_ -> Expect.equal 2 (List.length [ 1, 4 ]))
        ]
    ]

Custom expect

As we saw with the type signature of the test function, the only requirement for the passed assert function is that it returns an Expectation. This fact means that it is straightforward to implement our own. In this Christmas time, would it not be nice to assert that our numbers are 24? It could be done like this:

suite : Test
suite =
    describe "All our numbers are magical"
        [ test "8 * 3 equals 24"
            (\_ -> expectMagicalNumber (8 * 3))
        , test "12 * 2 equals 24"
            (\_ -> expectMagicalNumber 12 * 2)
        ]


expectMagicalNumber : Int -> Expectation
expectMagicalNumber number =
    if number == 24 then
        Expect.pass

    else
        Expect.fail (String.fromInt number ++ " is not 24..")

Summary

In today's article, we have looked into how we can write some simple unit tests in Elm. We learned about the type signature of the test function and took a peak of different modules in the Expect library. Furthermore, we saw how we could group tests together and how to write our own Expect-functions. A concept of elm-testing, which we did not mention is the powerful fuzz-testing library of elm-test.

If you are eager to start testing you Elm code, I encourage you to read the section 'Strategies for effective testing' in elm-test readme. Happy testing!

←Previous postNext post →