brian-watkins/elm-spec/1.0.1

NOTE: the latest version is 3.3.2

Spec.Witness

A Witness can be used to record information about the arguments passed to a Cmd-generating function.

If you use dependency inversion to decouple part of your program from the Cmd-generating functions it depends upon, you can use a Witness to prove that this part of your program works with the Cmd-generating function in the right way.

Let's say I have a Cmd-generating function that takes a score (an Int) and saves it to the server via some HTTP request. Let's further suppose I want to describe the behavior of my user interface, without worrying about the details of how a score is saved. In this case, I'll 'inject' the Cmd-generating function instead of calling it directly from my update function. This allows me to substitute a fake function during my spec.

Here's the update function:

update : (Int -> Cmd Msg) -> Msg -> Model -> (Model, Cmd Msg)
update scoreSaver msg model =
  case msg of
    SaveScore score ->
      ( model, scoreSaver score )
    ...

Now, I can write a spec that uses a witness to record the score passed to the injected scoreSaver function like so:

Spec.describe "saving the score"
[ Spec.scenario "successful save" (
    Spec.given (
      Spec.Setup.withInit (App.init testFlags)
        |> Spec.Setup.withView App.view
        |> Witness.forUpdate (\witness ->
          App.update <| \score ->
            Witness.log "saved-score"
              (Json.Encode.int score) witness
        )
    )
    |> Spec.when "the score is saved"
      [ Spec.Markup.target << by [ id "game-over-button" ]
      , Spec.Markup.Event.click
      ]
    |> it "saves the proper score" (
      Witness.observe "saved-score" (Json.Decode.int)
        |> Spec.expect (Spec.Claim.isList
          [ Spec.Claim.isEqual Debug.toString 28
          ]
        )
    )
  )
]
type Witness msg

Use a Witness to record information from inside a Cmd-generating function.

(Witness msg -> msg -> model -> ( model, Cmd msg ))
-> Setup model msg
-> Setup model msg

Set up the scenario with the update function from the program whose behavior is being described. You'll have access to a Witness to use in constructing this function.

See the example above.

log : String -> Value -> Witness msg -> Cmd msg

Create a Cmd that logs some information.

Provide the name of this witness and a JSON value with any information to be logged.

observe : String -> Decoder a -> Observer model (List a)

Observe the logs recorded by a witness.

Provide the name of the witness and a JSON decoder that can decode whatever value you need to observe.