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.
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.
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?
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)
When we have multiple tests, it make sense to group them. We can achieve that with the
describe "List" [ describe "Length" [ test "Empty list" (\_ -> Expect.equal 0 (List.length )) , test "With two elements" (\_ -> Expect.equal 2 (List.length [ 1, 4 ])) ] ]
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..")
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!