avh4 / elm-program-test / ProgramTest

A ProgramTest simulates the execution of an Elm program enabling you write high-level tests for your program. (Testing your programs at this level provides test coverage that is resilient even to drastic refactorings of your application architecture, and encourages tests that make it clear how end-users and external services will interact with your program.)

This module allows you to interact with your program by simulating user interactions and external events (like HTTP responses and ports), and making assertions about the HTML it renders and the external requests it makes.

Documentation index

The list below is an index into the API documentation for the assertion and simulation functions relevant to each topic:

Getting started

For a more detailed explanation of how to get started, see the elm-program-test guidebooks (the best one to start with is “Testing programs with interactive views”):

Creating


type ProgramTest model msg effect

A ProgramTest represents an Elm program, a current state for that program, information about external effects that have been produced by the program (such as pending HTTP requests, values sent to outgoing ports, etc), and a log of any errors that have occurred while simulating interaction with the program.

start : flags -> ProgramDefinition flags model msg effect -> ProgramTest model msg effect

Starts the given test program by initializing it with the given flags.

If your program uses Json.Encode.Value as its flags type, you may find withJsonStringFlags useful.

Creating program definitions

A ProgramDefinition (required to create a ProgramTest with start) can be created with one of the following functions that parallel the functions in elm/browser for creating programs.

createSandbox : { init : model, view : model -> Html msg, update : msg -> model -> model } -> ProgramDefinition () model msg ()

Creates a ProgramDefinition from the parts of a Browser.sandbox program.

See other create* functions below if the program you want to test does not use Browser.sandbox.

createElement : { init : flags -> ( model, effect ), view : model -> Html msg, update : msg -> model -> ( model, effect ) } -> ProgramDefinition flags model msg effect

Creates a ProgramTest from the parts of a Browser.element program.

See other create* functions below if the program you want to test does not use Browser.element.

If your program has subscriptions that you want to simulate, see withSimulatedSubscriptions.

createDocument : { init : flags -> ( model, effect ), view : model -> Browser.Document msg, update : msg -> model -> ( model, effect ) } -> ProgramDefinition flags model msg effect

Creates a ProgramTest from the parts of a Browser.document program.

See other create* functions if the program you want to test does not use Browser.document.

If your program has subscriptions that you want to simulate, see withSimulatedSubscriptions.

createApplication : { init : flags -> Url -> () -> ( model, effect ), view : model -> Browser.Document msg, update : msg -> model -> ( model, effect ), onUrlRequest : Browser.UrlRequest -> msg, onUrlChange : Url -> msg } -> ProgramDefinition flags model msg effect

Creates a ProgramTest from the parts of a Browser.application program.

See other create* functions if the program you want to test does not use Browser.application.

If your program has subscriptions that you want to simulate, see withSimulatedSubscriptions.

Note that Elm currently does not provide any way to create a Browser.Navigation.Key in tests, so this function uses () as the key type instead. For an example of how to test such a program, see NavigationKeyExample.elm and NavigationKeyExampleTest.elm.

createWorker : { init : flags -> ( model, effect ), update : msg -> model -> ( model, effect ) } -> ProgramDefinition flags model msg effect

Creates a ProgramTest from the parts of a Platform.worker program.

See other create* functions if the program you want to test does not use Platform.worker.

If your program has subscriptions that you want to simulate, see withSimulatedSubscriptions.


type ProgramDefinition flags model msg effect

Represents an unstarted program test. Use start to start the program being tested.

Options

The following functions allow you to configure your ProgramDefinition before starting it with start.

withBaseUrl : String -> ProgramDefinition flags model msg effect -> ProgramDefinition flags model msg effect

Sets the initial browser URL

You must set this when using createApplication, or when using clickLink and expectPageChange to simulate a user clicking a link with relative URL.

withJsonStringFlags : Json.Decode.Decoder flags -> ProgramDefinition flags model msg effect -> ProgramDefinition String model msg effect

Provides a convenient way to provide flags for a program that decodes flags from JSON. By providing the JSON decoder, you can then provide the flags as a JSON string when calling start.

withSimulatedEffects : (effect -> SimulatedEffect msg) -> ProgramDefinition flags model msg effect -> ProgramDefinition flags model msg effect

This allows you to provide a function that lets ProgramTest simulate effects that would become Cmds and Tasks when your app runs in production (this enables you to use simulateHttpResponse, advanceTime, etc.). For a detailed explanation and example of how to set up tests that use simulated effects, see the “Testing programs with Cmds” guidebook.

You only need to use this if you need to simulate HTTP requests, outgoing ports, or the passing of time.

See the SimulatedEffect.* modules in this package for functions that you can use to implement the required effect -> SimulatedEffect msg function for your effect type.


type alias SimulatedEffect msg =
SimulatedEffect msg

This represents an effect that elm-program-test is able to simulate. When using withSimulatedEffects you will provide a function that can translate your program's effects into SimulatedEffects. (If you do not use withSimulatedEffects, then ProgramTest will not simulate any effects for you.)

You can create SimulatedEffects using the following modules, which parallel the modules your real program would use to create Cmds and Tasks:


type alias SimulatedTask x a =
SimulatedEffect.SimulatedTask x a

Similar to SimulatedEffect, but represents a Task instead of a Cmd.

withSimulatedSubscriptions : (model -> SimulatedSub msg) -> ProgramDefinition flags model msg effect -> ProgramDefinition flags model msg effect

This allows you to provide a function that lets ProgramTest simulate subscriptions that would be Subs when your app runs in production (this enables you to use simulateIncomingPort, etc.). You only need to use this if you need to simulate subscriptions in your test. For a detailed explanation and example of how to set up tests that use simulated subscriptions, see the “Testing programs with ports” guidebook.

The function you provide should be similar to your program's subscriptions function but return SimulatedSubs instead of Subs. See the SimulatedEffect.* modules in this package for functions that you can use to implement the required model -> SimulatedSub msg function.


type alias SimulatedSub msg =
SimulatedEffect.SimulatedSub msg

This represents a subscription that elm-program-test is able to simulate. When using withSimulatedSubscriptions you will provide a function that is similar to your program's subscriptions function but that returns SimulatedSubs instead Subs. (If you do not use withSimulatedSubscriptions, then ProgramTest will not simulate any subscriptions for you.)

You can create SimulatedSubs using the following modules:

Ending a test

done : ProgramTest model msg effect -> Expectation

Ends a ProgramTest, reporting any errors that occurred.

You can also end a ProgramTest using any of the functions starting with expect*. In fact, you should prefer using one of the expect* functions when possible, as doing so will make the intent of your test more clear.

Inspecting and interacting with HTML

Inspecting HTML

expectViewHas : List Test.Html.Selector.Selector -> ProgramTest model msg effect -> Expectation

A simpler way to assert that a ProgramTest's view matches a given selector.

expectViewHas [...selector...] is the same as expectView (Test.Html.Query.has [...selector...]).

If you want to interact with the program more after this assertion, see ensureViewHas.

expectViewHasNot : List Test.Html.Selector.Selector -> ProgramTest model msg effect -> Expectation

A simpler way to assert that a ProgramTest's view does not match a given selector.

expectViewHasNot [...selector...] is the same as expectView (Test.Html.Query.hasNot [...selector...]).

If you want to interact with the program more after this assertion, see ensureViewHasNot.

expectView : (Test.Html.Query.Single msg -> Expectation) -> ProgramTest model msg effect -> Expectation

Makes an assertion about the current state of a ProgramTest's view.

If you want to interact with the program more after this assertion, see ensureView.

ensureViewHas : List Test.Html.Selector.Selector -> ProgramTest model msg effect -> ProgramTest model msg effect

See the documentation for expectViewHas. This is the same except that it returns a ProgramTest instead of an Expectation so that you can interact with the program further after this assertion.

You should prefer expectViewHas when possible, as having a single assertion per test can make the intent of your tests more clear.

ensureViewHasNot : List Test.Html.Selector.Selector -> ProgramTest model msg effect -> ProgramTest model msg effect

See the documentation for expectViewHasNot. This is the same except that it returns a ProgramTest instead of an Expectation so that you can interact with the program further after this assertion.

You should prefer expectViewHasNot when possible, as having a single assertion per test can make the intent of your tests more clear.

ensureView : (Test.Html.Query.Single msg -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect

See the documentation for expectView. This is the same except that it returns a ProgramTest instead of an Expectation so that you can interact with the program further after this assertion.

You should prefer expectView when possible, as having a single assertion per test can make the intent of your tests more clear.

Simulating user input

clickButton : String -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates clicking a button.

This function will find and click a <button> HTML node containing the given buttonText.

It will also try to find and click elements with the accessibility label role="button".

If the button is disabled the test will fail.

clickLink : String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates clicking a <a href="..."> link.

The parameters are:

  1. The text of the <a> tag (which is the link text visible to the user).

  2. The href of the <a> tag.

    NOTE: After eeue56/elm-html-test#52 is resolved, a future release of this package will remove the href parameter.

Note for testing single-page apps: if the target <a> tag has an onClick handler, then the message produced by the handler will be processed and the href will not be followed. NOTE: Currently this function cannot verify that the onClick handler sets preventDefault, but this will be done in the future after https://github.com/eeue56/elm-html-test/issues/63 is resolved.

fillIn : String -> String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates replacing the text in an input field labeled with the given label.

  1. The id of the input field (which must match both the id attribute of the target input element, and the for attribute of the label element), or "" if the <input> is a descendant of the <label>.

    NOTE: After eeue56/elm-html-test#52 is resolved, a future release of this package will remove this parameter.

  2. The label text of the input field.

  3. The text that will be entered into the input field.

There are a few different ways to accessibly label your input fields so that fillIn will find them:

If you need to target a <textarea> that does not have a label, see fillInTextarea.

If you need more control over finding the target element or creating the simulated event, see simulateDomEvent.

fillInTextarea : String -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates replacing the text in a <textarea>.

This function expects that there is only one <textarea> in the view. If your view has more than one <textarea>, prefer adding associated <label> elements and use fillIn. If you cannot add <label> elements see within.

If you need more control over finding the target element or creating the simulated event, see simulateDomEvent.

check : String -> String -> Basics.Bool -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates setting the value of a checkbox labeled with the given label.

The parameters are:

  1. The id of the input field (which must match both the id attribute of the target input element, and the for attribute of the label element), or "" if the <input> is a descendant of the <label>.

    NOTE: After eeue56/elm-html-test#52 is resolved, a future release of this package will remove this parameter.

  2. The label text of the input field

  3. A Bool indicating whether to check (True) or uncheck (False) the checkbox.

NOTE: In the future, this will be generalized to work with aria accessibility attributes in addition to working with standard HTML label elements.

If you need more control over finding the target element or creating the simulated event, see simulateDomEvent.

selectOption : String -> String -> String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates choosing an option with the given text in a select with a given label

The parameters are:

  1. The id of the <select> (which must match both the id attribute of the target select element, and the for attribute of the label element), or "" if the <select> is a descendant of the <label>.

    NOTE: After eeue56/elm-html-test#52 is resolved, a future release of this package will remove this parameter.

  2. The label text of the select.

  3. The value of the <option> that will be chosen.

    NOTE: After eeue56/elm-html-test#51 is resolved, a future release of this package will remove this parameter.

  4. The user-visible text of the <option> that will be chosen.

Example: If you have a view like the following,

import Html
import Html.Attributes exposing (for, id, value)
import Html.Events exposing (on, targetValue)

Html.div []
    [ Html.label [ for "pet-select" ] [ Html.text "Choose a pet" ]
    , Html.select
        [ id "pet-select", on "change" targetValue ]
        [ Html.option [ value "dog" ] [ Html.text "Dog" ]
        , Html.option [ value "hamster" ] [ Html.text "Hamster" ]
        ]
    ]

you can simulate selecting an option like this:

ProgramTest.selectOption "pet-select" "Choose a pet" "dog" "Dog"

If you need more control over finding the target element or creating the simulated event, see simulateDomEvent.

Simulating user input (advanced)

simulateDomEvent : (Test.Html.Query.Single msg -> Test.Html.Query.Single msg) -> ( String, Json.Encode.Value ) -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates a custom DOM event.

NOTE: If there is another, more specific function (see “Simulating user input”) that does what you want, prefer that instead, as you will get the benefit of better error messages.

The parameters are:

  1. A function to find the HTML element that responds to the event (typically this will be a call to Test.Html.Query.find [ ...some selector... ])
  2. The event to simulate (see Test.Html.Event "Event Builders")

within : (Test.Html.Query.Single msg -> Test.Html.Query.Single msg) -> (ProgramTest model msg effect -> ProgramTest model msg effect) -> ProgramTest model msg effect -> ProgramTest model msg effect

Focus on a part of the view for a particular operation.

For example, if your view produces the following HTML:

<div>
  <div id="sidebar">
    <button>Submit</button>
  </div>
  <div id="content">
    <button>Submit</button>
  </div>
</div>

then the following will allow you to simulate clicking the "Submit" button in the sidebar (simply using clickButton "Submit" would fail because there are two buttons matching that text):

import Test.Html.Query as Query
import Test.Html.Selector exposing (id)

programTest
    |> ProgramTest.within
        (Query.find [ id "sidebar" ])
        (ProgramTest.clickButton "Submit")
    |> ...

Inspecting and simulating HTTP requests and responses

Inspecting HTTP requests

expectHttpRequestWasMade : String -> String -> ProgramTest model msg effect -> Expectation

Asserts that an HTTP request to the specific url and method has been made.

The parameters are:

  1. The HTTP method of the expected request (typically "GET" or "POST")
  2. The absolute URL of the expected request

For example:

...
    |> expectHttpRequestWasMade "GET" "https://example.com/api/data"

If you want to check the headers or request body, see expectHttpRequest. If you expect multiple requests to have been made to the same endpoint, see expectHttpRequests.

NOTE: You must use withSimulatedEffects before you call start to be able to use this function.

If you want to interact with the program more after this assertion, see ensureHttpRequestWasMade.

expectHttpRequest : String -> String -> (Test.Http.HttpRequest msg msg -> Expectation) -> ProgramTest model msg effect -> Expectation

Allows you to check the details of a pending HTTP request.

See the “Expectations” section of Test.Http for functions that might be helpful in create an expectation on the request.

If you only care about whether the a request was made to the correct URL, see expectHttpRequestWasMade.

...
    |> expectHttpRequest "POST"
        "https://example.com/save"
        (.body >> Expect.equal """{"content":"updated!"}""")

If you expect multiple requests to have been made to the same endpoint, see expectHttpRequests.

NOTE: You must use withSimulatedEffects before you call start to be able to use this function.

If you want to interact with the program more after this assertion, see ensureHttpRequest.

expectHttpRequests : String -> String -> (List (Test.Http.HttpRequest msg msg) -> Expectation) -> ProgramTest model msg effect -> Expectation

Allows you to check the details of pending HTTP requests.

See the “Expectations” section of Test.Http for functions that might be helpful in create an expectation on the request.

If your program will only have a single pending request to any particular URL, you can use the simpler expectHttpRequest (singular) or expectHttpRequestWasMade instead.

...
    |> expectHttpRequests "POST"
        "https://example.com/save"
        (List.map .body >> Expect.equal ["""body1""", """body2"""])

NOTE: You must use withSimulatedEffects before you call start to be able to use this function.

If you want to interact with the program more after this assertion, see ensureHttpRequests.

ensureHttpRequestWasMade : String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect

See the documentation for expectHttpRequestWasMade. This is the same except that it returns a ProgramTest instead of an Expectation so that you can interact with the program further after this assertion.

You should prefer expectHttpRequestWasMade when possible, as having a single assertion per test can make the intent of your tests more clear.

ensureHttpRequest : String -> String -> (Test.Http.HttpRequest msg msg -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect

See the documentation for expectHttpRequest. This is the same except that it returns a ProgramTest instead of an Expectation so that you can interact with the program further after this assertion.

You should prefer expectHttpRequest when possible, as having a single assertion per test can make the intent of your tests more clear.

ensureHttpRequests : String -> String -> (List (Test.Http.HttpRequest msg msg) -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect

See the documentation for expectHttpRequests. This is the same except that it returns a ProgramTest instead of an Expectation so that you can interact with the program further after this assertion.

You should prefer expectHttpRequests when possible, as having a single assertion per test can make the intent of your tests more clear.

Simulating HTTP responses

simulateHttpOk : String -> String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates an HTTP 200 response to a pending request with the given method and url.

The parameters are:

  1. The HTTP method of the request to simulate a response for (typically "GET" or "POST")
  2. The URL of the request to simulate a response for
  3. The response body for the simulated response

For example:

...
    |> simulateHttpOk "GET"
        "https://example.com/time.json"
        """{"currentTime":1559013158}"""
    |> ...

If you need to simulate an error, a response with a different status code, or a response with response headers, see simulateHttpResponse.

If you want to check the request headers or request body, use ensureHttpRequest immediately before using simulateHttpOk.

NOTE: You must use withSimulatedEffects before you call start to be able to use this function.

simulateHttpResponse : String -> String -> Http.Response String -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates a response to a pending HTTP request. The test will fail if there is no pending request matching the given method and url.

The parameters are:

  1. The HTTP method of the request to simulate a response for (typically "GET" or "POST")
  2. The URL of the request to simulate a response for
  3. The Http.Response value for the simulated response. You may find it helpful to see the “Responses” section in Test.Http for convenient ways to create Http.Response values.

For example:

...
    |> simulateHttpResponse "GET"
        "https://example.com/time.json"
        Test.Http.networkError
    |> ...
    |> simulateHttpResponse "POST"
        "https://example.com/api/v1/process_data"
        (Test.Http.httpResponse
            { statusCode : 204
            , headers : [ ( "X-Procesing-Time", "1506ms") ]
            , body : ""
            }
        )

If you are simulating a 200 OK response and don't need to provide response headers, you can use the simpler simulateHttpOk.

If you want to check the request headers or request body, use ensureHttpRequest immediately before using simulateHttpResponse.

If your program will make multiple pending requests to the same URL, see simulateHttpResponseAdvanced.

NOTE: You must use withSimulatedEffects before you call start to be able to use this function.

simulateHttpResponseAdvanced : String -> String -> Basics.Int -> Http.Response String -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates a response to one of several pending HTTP requests made to a given endpoint.

This is the same as simulateHttpResponse, except that the additional Int parameter specificies which request to resolve if multiple requests to the same method/URL are pending.

Simulating time

advanceTime : Basics.Int -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates the passing of time. The Int parameter is the number of milliseconds to simulate. This will cause any pending Task.sleeps to trigger if their delay has elapsed.

NOTE: You must use withSimulatedEffects before you call start to be able to use this function.

Inspecting and simulating ports

Inspecting outgoing ports

expectOutgoingPortValues : String -> Json.Decode.Decoder a -> (List a -> Expectation) -> ProgramTest model msg effect -> Expectation

Lets you assert on the values that the program being tested has sent to an outgoing port.

The parameters are:

  1. The name of the port
  2. A JSON decoder corresponding to the type of the port
  3. A function that will receive the list of values sent to the port since the start of the test (or since the last use of ensureOutgoingPortValues) and return an Expectation

For example:

...
    |> expectOutgoingPortValues
        "saveApiTokenToLocalStorage"
        Json.Decode.string
        (Expect.equal [ "975774a26612", "920facb1bac0" ])

For a more detailed explanation and example, see the “Testing programs with ports” guidebook.

NOTE: You must use withSimulatedEffects before you call start to be able to use this function.

If you want to interact with the program more after this assertion, see ensureOutgoingPortValues.

ensureOutgoingPortValues : String -> Json.Decode.Decoder a -> (List a -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect

See the documentation for expectOutgoingPortValues. This is the same except that it returns a ProgramTest instead of an Expectation so that you can interact with the program further after this assertion.

You should prefer expectOutgoingPortValues when possible, as having a single assertion per test can make the intent of your tests more clear.

Simulating incoming ports

simulateIncomingPort : String -> Json.Encode.Value -> ProgramTest model msg effect -> ProgramTest model msg effect

Lets you simulate a value being sent to the program being tested via an incoming port.

The parameters are:

  1. The name of the port
  2. The JSON representation of the incoming value

For example, here we are simulating the program receiving a list of strings on the incoming port port resultsFromJavascript : (List String -> msg) -> Sub msg:

...
    |> ProgramTest.simulateIncomingPort
        "resultsFromJavascript"
        (Json.Encode.list Json.Encode.string
            [ "Garden-path sentences can confuse the reader." ]
        )

For a more detailed explanation and example, see the “Testing programs with ports” guidebook.

NOTE: You must use withSimulatedSubscriptions before you call start to be able to use this function.

Browser navigation

Browser assertions

expectPageChange : String -> ProgramTest model msg effect -> Expectation

Asserts that the program ended by navigating away to another URL.

The parameter is:

  1. The expected URL that the program should have navigated away to.

If your program is an application that manages URL changes (created with createApplication), then you probably want expectBrowserUrl instead.

expectBrowserUrl : (String -> Expectation) -> ProgramTest model msg effect -> Expectation

Asserts on the current value of the browser URL bar in the simulated test environment.

The parameter is:

  1. A function that asserts on the current URL. Typically you will use Expect.equal with the exact URL you expect.

If your program is not an application that manages URL changes and you want to assert that the user clicked a link that goes to an external web page, then you probably want expectPageChange instead.

expectBrowserHistory : (List String -> Expectation) -> ProgramTest model msg effect -> Expectation

Asserts on the current browser history in the simulated test environment. This only makes sense if you are using withSimulatedEffects and the function you provide to it produces SimulatedEffect.Navigation.replaceUrl or SimulatedEffect.Navigation.pushUrl for one or more of your effects. The previous URL is added to the simulated browser history whenever a pushUrl effect is simulated.

The parameter is:

  1. A function that asserts on the current browser history (most recent at the head) to an expectation.

Example: If there's only one expected item in the history or if you want check the complete history since the start of the test, use this with Expect.equal

createApplication { ... }
    |> withBaseUrl "https://example.com/resource/123"
    |> start ()
    |> clickButton "Details"
    |> expectBrowserHistory (Expect.equal [ "https://example.com/resource/123/details" ])

Example: If there might be multiple items in the history and you only want to check the most recent item:

createApplication { ... }
    |> withBaseUrl "https://example.com/resource/123"
    |> start ()
    |> clickButton "Details"
    |> clickButton "Calendar"
    |> expectBrowserHistory (List.head >> Expect.equal (Just "https://example.com/resource/123/calendar"))

If you need to assert on the current URL, see expectBrowserUrl.

ensureBrowserUrl : (String -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect

See the documentation for expectBrowserUrl. This is the same except that it returns a ProgramTest instead of an Expectation so that you can interact with the program further after this assertion.

You should prefer expectBrowserUrl when possible, as having a single assertion per test can make the intent of your tests more clear.

ensureBrowserHistory : (List String -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect

See the documentation for expectBrowserHistory. This is the same except that it returns a ProgramTest instead of an Expectation so that you can interact with the program further after this assertion.

You should prefer expectBrowserHistory when possible, as having a single assertion per test can make the intent of your tests more clear.

Simulating browser interactions

routeChange : String -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulates a route change event (which would happen when your program is a Browser.application and the user manually changes the URL in the browser's URL bar).

The parameter may be an absolute URL or relative URL.

Low-level functions

You should avoid the functions below when possible, but you may find them useful to test things that are not yet directly supported by elm-program-test.

Low-level functions for Msgs and Models

update : msg -> ProgramTest model msg effect -> ProgramTest model msg effect

Advances the state of the ProgramTest by applying the given msg to your program's update function (provided when you created the ProgramTest).

This can be used to simulate events that can only be triggered by commands (Cmd) and subscriptions (Sub) (i.e., that cannot be triggered by user interaction with the view).

NOTE: When possible, you should prefer Simulating user input, Simulating HTTP responses, or (if neither of those support what you need) simulateLastEffect, as doing so will make your tests more resilient to changes in your program's implementation details.

expectModel : (model -> Expectation) -> ProgramTest model msg effect -> Expectation

Make an assertion about the current state of a ProgramTest's model.

When possible, you should prefer making assertions about the rendered view (see expectView) or external requests made by your program (see expectHttpRequest, expectOutgoingPortValues), as testing at the level that users and external services interact with your program will make your tests more resilient to changes in the private implementation of your program.

Low-level functions for effects

expectLastEffect : (effect -> Expectation) -> ProgramTest model msg effect -> Expectation

Makes an assertion about the last effect produced by a ProgramTest's program.

NOTE: If you are asserting about HTTP requests or outgoing ports, you should prefer more specific functions designed for that purpose. You can find links to the relevant documentation in the documentation index.

If you want to interact with the program more after this assertion, see ensureLastEffect.

ensureLastEffect : (effect -> Expectation) -> ProgramTest model msg effect -> ProgramTest model msg effect

See the documentation for expectLastEffect. This is the same except that it returns a ProgramTest instead of an Expectation so that you can interact with the program further after this assertion.

You should prefer expectLastEffect when possible, as having a single assertion per test can make the intent of your tests more clear.

simulateLastEffect : (effect -> Result String (List msg)) -> ProgramTest model msg effect -> ProgramTest model msg effect

Simulate the outcome of the last effect produced by the program being tested by providing a function that can convert the last effect into msgs.

The function you provide will be called with the effect that was returned by the most recent call to update or init in the ProgramTest.

NOTE: If you are simulating HTTP responses, you should prefer more specific functions designed for that purpose. You can find links to the relevant documentation in the documentation index.

Custom assertions

These functions may be useful if you are writing your own custom assertion functions.

fail : String -> String -> ProgramTest model msg effect -> ProgramTest model msg effect

fail can be used to report custom errors if you are writing your own convenience functions to deal with program tests.

For example, this function checks for a particular structure in the program's view, but will also fail the ProgramTest if the expectedCount parameter is invalid:

expectNotificationCount : Int -> ProgramTest model msg effect -> Expectation
expectNotificationCount expectedCount programTest =
    if expectedCount <= 0 then
        programTest
            |> ProgramTest.fail "expectNotificationCount"
                ("expectedCount must be positive, but was: " ++ String.fromInt expectedCount)

    else
        programTest
            |> expectViewHas
                [ Test.Html.Selector.class "notifications"
                , Test.Html.Selector.text (toString expectedCount)
                ]

If you are writing a convenience function that is creating a program test, see createFailed.

createFailed : String -> String -> ProgramTest model msg effect

createFailed can be used to report custom errors if you are writing your own convenience functions to create program tests.

NOTE: if you are writing a convenience function that takes a ProgramTest as input, you should use fail instead, as it provides more context in the test failure message.

The parameters are:

  1. The name of your helper function (displayed in failure messages)
  2. The failure message (also included in the failure message)

For example:

-- JsonSchema and MyProgram are imaginary modules for this example


import JsonSchema exposing (Schema, validateJsonSchema)
import MyProgram exposing (Model, Msg)
import ProgramTest exposing (ProgramTest)

createWithValidatedJson : Schema -> String -> ProgramTest Model Msg (Cmd Msg)
createWithValidatedJson schema json =
    case validateJsonSchema schema json of
        Err message ->
            ProgramTest.createFailed
                "createWithValidatedJson"
                ("JSON schema validation failed:\n" ++ message)

        Ok () ->
            ProgramTest.createElement
                { init = MyProgram.init
                , update = MyProgram.update
                , view = MyProgram.view
                }
                |> ProgramTest.start json

getOutgoingPortValues : String -> ProgramTest model msg effect -> Result (ProgramTest model msg effect) (List Json.Encode.Value)

This can be used for advanced helper functions where you want to continue a test but need the data sent out through ports later in your test.

NOTE: If you do not need this advanced functionality, prefer expectOutgoingPortValues instead.

Elm language workaround

elmMajorVersionHack_4 : ()

Unused. This is a workaround for issues with publishing this package.

Until Allow package authors to request major versions if API is the same, but behavior is not · Issue #2099 · elm/compiler, Inaccuracy: Elm does not enforce semantic versioning · Issue #868 · elm/elm-lang.org, and elm diff / publish is unaware of breaking visual changes · Issue #2145 · elm/compiler are addressed, this is required to allow the major version of this package to be updated.