This week's about tooling, so let's have a look at what I usually consider the most important tool for a given language: a test framework. We'll be using elm-test. Let's get started.
We'll start out in the frogger repo, which
I've tagged with
We'll start out by making a
mkdir tests cd tests
elm-package install elm-community/elm-test
Now we'll make a
We'll just copy the example Tests.elm file from the elm-test repo for now:
module Tests exposing (..) import List import ElmTest exposing (..) tests : List Test tests = [ 0 `equals` 0 , test "pass" <| assert True , test "fail" <| assertNotEqual True False ] ++ (List.map defaultTest <| assertionList [1..10] [1..10]) consoleTests : Test consoleTests = suite "All Tests" tests main = runSuite consoleTests
We'll look at it in detail in a moment. For now, let's just run the tests.
You can run them in the console out of the gate in elm-test since it was updated for 0.17. First, we have to compile a js file:
elm-make Tests.elm --output tests.js
Now we can run it with node:
1 suites run, containing 13 tests All tests passed : ""
Now let's look at the elm file to see how you write tests:
-- We make a normal module. module Tests exposing (..) -- We import ElmTest import List import ElmTest exposing (..) -- We'll make a tests function that produces a list of tests tests : List Test tests = -- `equals` will make a test that asserts equality between two arguments. Here, -- we're using it in infix style. [ 0 `equals` 0 -- `test` takes a test name and an assertion. Here a backwards pipe is being -- used to avoid parenthesis, but you can also just wrap the assertion in -- parens. -- `assert` will produce a passing test if its argument is True , test "pass" <| assert True -- `assertNotEqual` will pass if the arguments are not equal , test "fail" <| assertNotEqual True False ] -- `defaultTest` will generate the test name for you based on the inputs. -- Here, we map that function across a list of assertions. -- `assertionList` takes two lists of equal length and produces a list of -- assertions that each matching element in the lists is equal. -- So here we're just mapping over those assertions with `defaultTest` to -- produce a list of tests for those assertions with auto-generated names. ++ (List.map defaultTest <| assertionList [1..10] [1..10]) -- `suite` takes a name and a list of tests, and produces a new test that wraps -- all of those in one chunk of execution. consoleTests : Test consoleTests = suite "All Tests" tests -- And our main function passes our suite to the `runSuite` function, which will -- run a suite of tests as a program. main = runSuite consoleTests
Let's play around a bit to get comfortable with everything. We'll extract that assertionList line into its own function:
tests : List Test tests = [ 0 `equals` 0 , test "pass" (assert True) , test "fail" <| assertNotEqual True False ] assertionListTests : List Test assertionListTests = (List.map defaultTest <| assertionList [1..10] [1..10])
basicTests : List Test basicTests = -- ...
basicTestSuite : Test basicTestSuite = suite "Basic Tests" basicTests
We'll add a
listTestSuite : Test listTestSuite = suite "List Tests" assertionListTests
We'll make an
allTestSuite that wraps each of those suites into a parent
suite, showing that since suites are of type
Test they can be trivially
allTestSuite : Test allTestSuite = suite "All Tests" [ basicTestSuite, listTestSuite ]
And we'll update our main to run the new parent test suite:
main = runSuite allTestSuite
Let's go ahead and compile it and make sure it all runs still:
elm-make Tests.elm --output tests.js
And we'll run it again:
3 suites run, containing 13 tests All tests passed : ""
So here we can see that the output is aware of how many suites we have.
The only function we really haven't covered is
lazyAssert. It takes a
function from the unit to a Bool, but delays execution. We'll add one just for
, test "lazy" <| lazyAssert (\_ -> 6 > 5)
Next, let's go back to the root of the project and add a basic script to run our tests:
cd .. echo "pushd tests && elm-make Tests.elm --output tests.js && node tests.js && popd" > ./test chmod u+x test
Now we can run it:
Finally, it's always nice to set up CI for your tests. We can do this with Travis CI fairly easily. Make a .travis.yml file:
language: node_js node_js: - "5" install: - npm install -g elm - elm-package install -y - pushd tests && elm-package install -y && popd script: - cd tests && elm-make Tests.elm --output tests.js && node tests.js
If you push that up, travis will run the tests for every commit and report back to you. You can see an example build here
Alright, in today's episode we saw how to use
elm-test to write our tests and
run them easily on the command line. We also saw how to run our tests with
Travis CI. I hope you enjoyed it. See you soon!