10

I am writing an Elm test suite and want to check a function's output using a list of known good input/output pairs, stored in an external file. I can choose the format of the external file, so I could use JSON for example, but I need to keep it separate because it is accessed from other languages. (I am basically ensuring that the Elm version of the function matches other versions).

I do not want to hard-code the values into the Elm test module. Is there a way to do this with Elm and elm-test?

spookylukey
  • 6,380
  • 1
  • 31
  • 34
  • 2
    I have the same need, but know of no solution, other than some sort of script to copy the json as a string into .elm file template. I asked once on thr #testing channel on Slack but got no answer - try again...? – Simon H Jan 22 '18 at 18:28
  • 1
    I'd do exactly what Simon suggested. Add a step prior to running the test suite which merges an elm template which contains a replacement token/string with the JSON content; Basically, it's automated hard-coding. – Emmanuel Rosa Jan 22 '18 at 19:27

1 Answers1

4

I have come up with the following answer to my own question, which seems reasonable and satisfies all my requirements, but perhaps there is something better.

  1. Create an Elm Native module - see this tutorial.

  2. Inside it, load the data using any Node javascript functions e.g. fs = require('fs') and JSON.parse etc.

  3. Return the data as a simple Javascript object e.g. nested arrays of numbers etc.

  4. In the Elm test suite, this data will need to be handled as a Json Value, and decoded using Json.Decode.decodeValue

The full solution might look like this (Elm 0.18) - adapted from my real solution:

In tests/my_function_test_data.json:

[
    [0, 1, 2],
    [3, 4, 5]
]

In tests/Native/TestData.js:

var _user$project$Native_TestData = function () {
    var fs = require('fs');
    var path = require('path');
    var jsonPath = path.join(__dirname, '..', 'my_function_test_data.json');
    var myFunctionTestData = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
    return {
        myFunctionTestData: myFunctionTestData
    }
}();

In tests/MyTests.elm:

import Native.TestData

myFunctionTestData : List (List Float)
myFunctionTestData =
    JD.decodeValue (JD.list (JD.list JD.float)) Native.TestData.myFunctionTestData
        |> \v -> case v of
                     Ok val ->
                         val
                     Err msg ->
                         Debug.crash msg

This approach can be expanded in various ways (e.g. different formats, or pass a parameter to the native module to determine which file to load), and it avoids needing an extra step to generate source code modules.

spookylukey
  • 6,380
  • 1
  • 31
  • 34
  • Worth reminding readers that you also need to add `"native-modules": true` to your `test/elm-package.json` – Simon H Jan 27 '18 at 09:51
  • Any update on how to do this now that Native Modules are deprecated in 0.19? https://discourse.elm-lang.org/t/native-code-in-0-19/826 – advait Jan 31 '20 at 01:10
  • I'm afraid not - the changes in Elm 0.19 mean its the end of the line for me. I can't upgrade and I'm investigating alternatives (e.g. bucklescript-tea/philip2) – spookylukey Feb 01 '20 at 09:19