{"id":21447266,"url":"https://github.com/brian-watkins/elmer","last_synced_at":"2025-07-14T19:31:54.311Z","repository":{"id":17653636,"uuid":"69750080","full_name":"brian-watkins/elmer","owner":"brian-watkins","description":"Describe the behavior of Elm HTML applications","archived":false,"fork":false,"pushed_at":"2022-12-09T00:38:22.000Z","size":836,"stargazers_count":41,"open_issues_count":5,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-12T02:10:17.349Z","etag":null,"topics":["elm","testing"],"latest_commit_sha":null,"homepage":"","language":"Elm","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brian-watkins.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-01T16:36:51.000Z","updated_at":"2022-11-09T04:06:12.000Z","dependencies_parsed_at":"2023-01-11T19:39:49.720Z","dependency_job_id":null,"html_url":"https://github.com/brian-watkins/elmer","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brian-watkins%2Felmer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brian-watkins%2Felmer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brian-watkins%2Felmer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brian-watkins%2Felmer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brian-watkins","download_url":"https://codeload.github.com/brian-watkins/elmer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225994843,"owners_count":17556830,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elm","testing"],"created_at":"2024-11-23T03:09:31.399Z","updated_at":"2024-11-23T03:09:31.896Z","avatar_url":"https://github.com/brian-watkins.png","language":"Elm","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ELMER IS DEPRECATED\n\nThere will be no more updates to Elmer. It was fun while it lasted, but I'm pretty sure\nElmer won't work with Elm 0.19.1 or later versions, and I'm not planning to make any more updates.\n\n## Instead, you should use [elm-spec](https://package.elm-lang.org/packages/brian-watkins/elm-spec/latest/).\n\nElm-spec is a test framework for Elm that lets you describe the behavior of Elm programs, much like Elmer did.\nUnlike Elmer, elm-spec doesn't use kernel/native code so it has been published to the Elm package repository\nand should be more resilient to future changes in Elm.\n\n---------\n\n# Elmer\n\nElmer makes it easy to describe the behavior of Elm HTML applications. If you love TDD and\nyou love Elm, then you'll probably appreciate Elmer.\n\n\n### Why?\n\nBehavior-driven development is a great practice to follow when writing\napplications. If you describe the behavior of your app in code, it's easy to add\nnew features or refactor the code with confidence: just run the tests to see\nif your app still has all the behavior you've described in those tests.\n\nElm is a really great language for writing web applications. However, practicing BDD\nin Elm can be difficult. The two main functions in the Elm architecture -- `view` and\n`update` -- return opaque types, which cannot be inspected for testing purposes.\nEven so, calling `view` or `update` directly requires knowledge of an application's\nimplementation: the shape of its model, its messages, and so on. If writing\ntests requires knowledge of implementation details, you lose the biggest\nbenefit of writing tests in the first place: the ability to change your code\nwith confidence.\n\nElmer allows you to describe the behavior of your app without knowledge of\nimplementation details. It simulates the Elm architecture, calling `view`\nand `update` as necessary throughout the course of your test. It lets\nyou manage how commands and subscriptions are processed so you can\ndescribe the behavior of your app under whatever conditions you need. Elmer\nallows you to write tests first, which gives you the freedom and confidence\nto change your code later on.\n\n\n## Getting Started\n\nBecause Elmer uses some native Javascript code to accomplish its magic, you cannot install Elmer through the elm package repository. Instead, you can install Elmer with the `elmer-test` package on NPM. Follow these steps to TDD bliss ...\n\n\n### Install\n\nFirst, you'll need to install \n- Elm (0.19)\n- The latest version of the [node test runner](https://www.npmjs.com/package/elm-test)\nfor the elm test package that works with Elm 0.19 (`elm-test@elm0.19.0`)\n- This package, which you'll install via npm\n\nI recommend installing these dependencies locally in your project directory so you can track versions carefully. Here's the command to install all these at once:\n\n```\n$ npm install --save-dev elm elm-test@elm0.19.0 elmer-test\n```\n\nNow install the elm test library:\n\n```\n$ npx elm install elm-explorations/test\n```\n\n\n### Update the elm.json file\n\nIn your `elm.json` file, you'll need to manually add elmer to the `test-dependencies` section like so:\n\n```\n\"test-dependencies\": {\n  \"direct\": {\n    \"elm-explorations/test\": \"1.1.0\",\n    \"elm-explorations/elmer\": \"6.0.0\"\n  },\n  \"indirect\": {}\n}\n```\n\nThe latest version of Elmer is 6.0.0. Make sure the version number of elmer \nmatches the version number of the `elmer-test` NPM package.\n\nNotice the `indirect` section under `test-dependencies`. Elmer itself has the following dependencies:\n\n```\n\"dependencies\": {\n  \"elm/browser\": \"1.0.0 \u003c= v \u003c 2.0.0\",\n  \"elm/core\": \"1.0.0 \u003c= v \u003c 2.0.0\",\n  \"elm/html\": \"1.0.0 \u003c= v \u003c 2.0.0\",\n  \"elm/json\": \"1.0.0 \u003c= v \u003c 2.0.0\",\n  \"elm/random\": \"1.0.0 \u003c= v \u003c 2.0.0\",\n  \"elm/url\": \"1.0.0 \u003c= v \u003c 2.0.0\",\n  \"elm-explorations/test\": \"1.0.0 \u003c= v \u003c 2.0.0\"\n},\n```\n\nIf any of these dependencies are not already listed as direct or indirect dependencies of your app, you'll need to list these in the `indirect` section of your `test-dependencies`. \n\nIf you just try to run elm-test (see below) and you're missing any dependencies, the compiler will give you an error message. Take the missing dependencies it mentions and list them as indirect test dependencies.\n\n\n### Run\n\nNow that everything's in place, you're ready to write tests with Elmer. In order to run those tests, you'll need to set the `ELM_HOME` environment variable to the `home` directory under the `elmer-test` install. If you've installed `elmer-test` locally, the directory should look like this:\n\n```\n\u003cProject Home\u003e/node_modules/elmer-test/home\n```\n\nI recommend adding a test script to your `package.json` that sets the environment variable for you. The following will work on a Mac running bash:\n\n```\n\"scripts\": {\n  \"test\": \"ELM_HOME=$(pwd)/node_modules/elmer-test/home elm-test\"\n}\n```\n\nNote that `ELM_HOME` must be an absolute path (thus the `$(pwd)` in the test command).\n\n\n### Caveats\n\nThe `elm` command searches for test dependencies any time you invoke it (so, even if you aren't running tests). This means that you will need to set the `ELM_HOME` environment variable as described above, any time you invoke the `elm` command. For example, to build your app, you'll need to do something like:\n\n```\n$ ELM_HOME=$(pwd)/node_modules/elmer-test/home elm make src/Main.elm\n```\n\n\n## Releases\n\n#### 6.0.0\n- Removed `Elmer.Http` and the dependency on elm/http. `Elmer.Http` now lives \nin its own [package](https://github.com/brian-watkins/elmer-http) so it can be updated independently.\n- Provided new APIs useful for creating extensions and custom matchers. See `Elmer.Value`, \n`Elmer.Message`, `Elmer.Message.Failure`,`Elmer.Effects`, and `Elmer.Task`\n\n#### 5.0.1\n- Support for calling a spy across multiple test states\n\n#### 5.0.0\n- Revised `Elmer.Spy` api to make it simpler and to allow the compiler to do type checking when injecting a spy or providing a fake implementation. This should provide better feedback when working with spies\n- See the section at the end of this document on migrating tests from 4.0.0 to 5.0.x\n\n#### 4.0.0\n- Updated Elmer to work with Elm 0.19\n- Revised api for targeting Html elements to allow the compiler to provide better feedback\n- See the section at the end of this document on migrating tests from 3.3.1 to 4.0.0\n\n#### 3.3.1\n- Last version for Elm 0.18\n\n\n## Documentation\n\nRead the [latest documentation](https://elmer-test.cfapps.io/elmer).\n\nIf you're interested in Elmer for Elm 0.18, you should read the documentation for Elmer 3.3.1, which\nyou can find [here](https://elmer-test.cfapps.io/elmer/versions).\n\n\n## Describing Behavior\n\nWhile tests can be written with Elmer in a variety of ways, the goal is use Elmer to describe *behavior*\nrather than implementation details. What do I mean? Let's say that an application is a collection of \nbehaviors. Each behavior has some pre-conditions -- these are characteristics of the world *outside* the \napplication that must be true for the behavior to occur -- and some set of resulting post-conditions --\ncharacteristics of the world *outside* the application that are a consequence of the behavior. To\ndescribe the *behavior* of an application, then, is to describe all the relationships that hold between\nrelevant states of the world outside the application due to the use of that application. \n\nHere's an example of a behavior from some game application that displays high scores:\n\n- Given that there is an HTTP web service that responds with a 200 status and a JSON document\nthat lists the high scores in some format.\n- When the user starts the game application, then the high scores are displayed as list items in HTML.\n\nThis behavior links one state of the world -- where there is an HTTP web service that successfully\nreturns a JSON document in some known format -- and another -- where some HTML document contains\nseveral `\u003cli\u003e` elements whose text shows the high scores from the web service. \n\nTo describe this behavior, we should not care how the application accomplishes the mapping between\nthese two states. We only care about describing the two states. To make the test pass, we will need\nto provide some implementation, but the test gives us freedom to choose whatever implementation makes\nsense for us now. Most importantly, however, as we add new behaviors to our application, we will be\nable to refactor our code with confidence. No matter what implementation we end up with, we should\nstill be able to run this test and ensure that the same mapping between pre-conditions and post-conditions\nstill holds. \n\nLike I've said, you can use Elmer to write tests in a variety of ways, but I encourage you to\nwrite tests that describe *behavior* so that you can refactor your code with confidence later on. This means\nyour tests should know as little as they can about the implementation of your Elm application. Strive to write\ntests that do not know the shape of your model or the particular messages that flow through the update function.\nDon't unit test functions. Begin each test only with references to the functions that must exist -- `view`, `update`,\n`init` -- and use Elmer to describe the pre- and post-conditions associated with some behavior. \n\n\n### Create a TestState\n\nTo begin a test with Elmer, you need to generate a `TestState` value. There are a variety of ways to do this:\n\n- Use `givenElement`, `givenApplication`, `givenDocument`, or `givenWorker` from\nthe `Elmer.Program` module to test particular kinds of programs. In these cases, you'll\nuse `Elmer.Program.init` to provide an initial model and command.\n- Use `Elmer.given` to test an arbitrary model, view method, and update method.\n- Use `Elmer.Command.given` to test a command-generating function in isolation.\n\n\n### Working with HTML\n\nSince Elm is primarily designed for writing HTML applications, much of the work that goes into describing\nthe pre- and post-conditions that characterize some behavior will involve working with HTML elements.\n\nElmer allows you to simulate events on elements and examine the state of elements. In order to do either, \nyou'll need to first target an element.\n\n\n#### Targeting an Element\n\nUse `Elmer.Html.target` along with the functions from `Elmer.Html.Selector` to target an element. Here's\na partial test that targets all the `\u003cli\u003e` elements that are children of an `\u003col\u003e` with a class `scores`\nin the current view:\n\n```\nallTests : Test\nallTests =\n  describe \"My Fun Game\"\n  [ describe \"High Score Screen\"\n    [ test \"it shows the high scores\" \u003c|\n      \\() -\u003e\n        Elmer.Program.givenElement App.view App.update\n          |\u003e Elmer.Program.init (\\_ -\u003e App.init testFlags)\n          |\u003e Elmer.Html.target\n              \u003c\u003c Elmer.Html.Selector.childrenOf \n                [ Elmer.Html.Selector.tag \"ol\"\n                , Elmer.Html.Selector.class \"score-list\"\n                ]\n              \u003c\u003c Elmer.Html.Selector.by\n                [ Elmer.Html.Selector.tag \"li\" ]\n    ...\n```\n\nSee `Elmer.Html.Selector` for more examples of selectors. It's also possible to write your own.\n\n\n#### Taking action on an element\n\nOnce you target an element, that element is the subject of subsequent actions, until\nyou target another element. The following functions define actions on elements:\n\n+ Click events: `Elmer.Html.Event.click \u003ctestState\u003e`\n+ Input events: `Elmer.Html.Event.input \u003ctext\u003e \u003ctestState\u003e`\n+ Custom events: `Elmer.Html.Event.trigger \u003ceventName\u003e \u003ceventJson\u003e \u003ctestState\u003e`\n+ There are also events for mouse movements, and checking and selecting input elements. See\nthe [docs](https://elmer-test.cfapps.io/elmer) for more information.\n\n\n#### Element Matchers\n\nYou can make expectations about targeted elements with the `Elmer.Html.expect` function.\n\nFirst, specify whether you want to match against a single element (with `element`)\nor a list of elements (with `elements`).\nThen you provide the appropriate matchers for the element or the list. You can also\nexpect that an element exists with the `elementExists` matcher.\n\nSee `Elmer.Html.Matchers` for a full list of matchers. Let's add to the example above to make\nan expectation about the elements we targeted. We'll use `Elmer.expectAll` to chain together several\nassertions. We'll expect that the list of `\u003cli\u003e` we've targeted has 2 elements, with the first containing\ntext of \"700 Points\" and the second \"900 Points\". \n\n```\nallTests : Test\nallTests =\n  describe \"My Fun Game\"\n  [ describe \"High Score Screen\"\n    [ test \"it shows the high scores\" \u003c|\n      \\() -\u003e\n        Elmer.Program.givenElement App.view App.update\n          |\u003e Elmer.Program.init (\\_ -\u003e App.init testFlags)\n          |\u003e Elmer.Html.target\n              \u003c\u003c Elmer.Html.Selector.childrenOf \n                [ Elmer.Html.Selector.tag \"ol\"\n                , Elmer.Html.Selector.class \"score-list\"\n                ]\n              \u003c\u003c Elmer.Html.Selector.by\n                [ Elmer.Html.Selector.tag \"li\" ]\n          |\u003e Elmer.Html.expect (Elmer.Html.Matchers.elements \u003c|\n              Elmer.expectAll\n              [ Elmer.hasLength 2\n              , Elmer.atIndex 0 \u003c| Elmer.Html.Matchers.hasText \"700 Points\"\n              , Elmer.atIndex 1 \u003c| Elmer.Html.Matchers.hasText \"900 Points\"\n              ]\n            )\n    ]\n  ]\n```\n\n\n### Commands\n\nCommands describe actions to be performed by the Elm runtime; the result of a command depends on the state of the\nworld outside the Elm application. Elmer simulates the Elm runtime in order to\nfacilitate testing, but it is not intended to replicate the Elm runtime's ability to carry out commands.\nInstead, Elmer allows you to specify what effect should result from running a command. This is one important\nway that Elmer allows you to describe the conditions that characterize an application behavior.\n\n\n#### Faking Effects\n\nSuppose there is a function `f : a -\u003e b -\u003e Cmd msg` that takes two arguments and produces a command. In order\nto specify the effect of this command in our tests, we will *override* occurrences of `f`\nwith a function we create in our tests. This function will generate a special command\nthat specifies the intended effect, and Elmer will process the result as if the original command were actually performed.\n\nFor a more concrete example, check out this [article](https://medium.com/@brian.watkins/test-driving-elm-with-elmer-649e2e7e02a8), which discusses how to fake\ncommands and subscriptions during a test.\n\nNote that while Elmer is not capable of processing any commands, it does support\nthe general operations on commands in the core `Platform.Cmd` module, namely, `batch` and `map`. So, you\ncan use these functions as expected in your application and Elmer should do the right thing.\n\nElmer provides built-in support for navigation commands. If you want to work with Http during your tests, check\nout the [Elmer.Http](https://github.com/brian-watkins/elmer-http) extension. \n\n\n#### Elmer.Navigation\n\nElmer provides support for functions in the [Browser.Navigation](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Navigation)\nmodule that allow you to handle navigation for single-page web applications.\n\nYou'll need to begin your test with `Elmer.Program.givenApplication` since only Elm\n'application' programs can handle navigation. Provide a reference to the messages\nthat handle new url requests and url changes along with the view and update functions. Then \nprovide `Elmer.Spy.use` with `Elmer.Navigation.spy`\nso that Elmer will be able to record and process location updates by overriding\n `Browser.Navigation.pushUrl` and `Browser.Navigation.replaceUrl`.\n\nWhen you call `Elmer.Program.init` you'll need to use `Elmer.Navigation.fakeKey` to\ngive your `init` function a `Browser.Navigation.Key` value. Here's an example of a test\nthat expects the location to change when an element is clicked. \n\n```\nElmer.Program.givenApplication App.OnUrlRequest App.OnUrlChange App.view App.update\n  |\u003e Elmer.Spy.use [ Elmer.Navigation.spy ]\n  |\u003e Elmer.Program.init (\\_ -\u003e App.init testFlags testUrl Elmer.Navigation.fakeKey)\n  |\u003e Elmer.Html.target \u003c\u003c by [ id \"some-element\" ]\n  |\u003e Elmer.Html.Event.click\n  |\u003e Elmer.Navigation.expectLocation \"http://mydomain.com/funStuff.html\"\n```\n\nYou can write an expectation about the current location with `Elmer.Navigation.expectLocation`.\n\nSee `tests/src/Elmer/TestApps/NavigationTestApp.elm` and `tests/src/Elmer/NavigationTests.elm` for\nexamples.\n\n\n#### Deferred Command Processing\n\nIt's often necessary to describe the behavior of an application while some command is running. For example,\none might want to show a progress indicator while an HTTP request is in process. Elmer provides\ngeneral support for deferred commands. Use `Elmer.Command.defer` to create a command that\nwill not be processed until `Elmer.resolveDeferred` is called. Note that all currently\ndeferred commands will be resolved when this function is called.\n\n\n#### Testing Commands in Isolation\n\nYou might want to test a command independently of any module that might use it. In that case,\nuse `Elmer.Command.given` and provide it with a function that generates the command you\nwant to test. This will initiate a `TestState` that simply records any messages that result when\nthe given command is processed. You can use the `Elmer.Command.expectMessages` function to\nmake any expectations about the messages received. For example, here's a test that expects a\ncertain message when a certain command is processed:\n\n```\nElmer.Command.given (\\_ -\u003e MyModule.myCommand MyTagger withSomeArgument)\n  |\u003e Elmer.Command.expectMessages (\\messages -\u003e\n    Expect.equal [ MyTagger \"Fun Result\" ]\n  )\n```\n\nYou can use `Elmer.Command.given` with spies as it makes sense. So, you might\nwrite a test that exercises a module with some function that needs to be stubbed (like a port command):\n\n```\nElmer.Command.given (\\_ -\u003e MyModule.sendRequest MyTagger someArgument)\n  |\u003e Elmer.Spy.use [ someSpy ]\n  |\u003e Elmer.Command.expectMessages (\\messages -\u003e\n    Expect.equal [ MyTagger \"Fun Result\" ]\n  )\n```\n\n\n### Subscriptions\n\nUsing subscriptions, your application can register to be notified when certain effects occur.\nTo describe the behavior of an application that has subscriptions, you'll need to do these things:\n\n1. Override the function that generates the subscription using `Elmer.Spy.create` along with\n`Elmer.Spy.andCallFake`\nand replace it with a fake subscription using `Elmer.Subscription.fake`\n2. Register the subscriptions using `Elmer.Subscription.with`\n2. Simulate the effect you've subscribed to receive with `Elmer.Subscription.send`\n\nHere's an example test:\n\n```\ntimeSubscriptionTest : Test\ntimeSubscriptionTest =\n  describe \"when a time effect is received\"\n  [ test \"it prints the number of seconds\" \u003c|\n    \\() -\u003e\n      let\n        timeSpy =\n          Elmer.Spy.observe (\\_ -\u003e Time.every)\n            |\u003e Elmer.Spy.andCallFake (\\_ tagger -\u003e\n              Elmer.Subscription.fake \"timeEffect\" tagger\n            )\n      in\n        Elmer.given App.defaultModel App.view App.update\n          |\u003e Elmer.Spy.use [ timeSpy ]\n          |\u003e Elmer.Subscription.with (\\() -\u003e App.subscriptions)\n          |\u003e Elmer.Subscription.send \"timeEffect\" (Time.millisToPosix 3000)\n          |\u003e Elmer.Html.target \u003c\u003c by [ id \"num-seconds\" ]\n          |\u003e Elmer.Html.expect (\n              Elmer.Html.Matchers.element \u003c|\n                Elmer.Html.Matchers.hasText \"3 seconds\"\n             )\n  ]\n```\n\nFor a more complete example, check out this [article](https://medium.com/@brian.watkins/test-driving-elm-with-elmer-649e2e7e02a8).\n\n\n### Ports\n\nYou can manage ports during your test in just the same way you would manage any\ncommand or subscription.\n\nSuppose you have a port that sends data to Javascript:\n\n```\nport module MyModule exposing (..)\n\nport sendData : String -\u003e Cmd msg\n```\n\nYou can create a spy for this function just like you would for any command-generating\nfunction:\n\n```\nElmer.Spy.observe (\\_ -\u003e MyModule.sendData)\n  |\u003e Elmer.Spy.andCallFake (\\_ -\u003e Cmd.none)\n```\n\nNote that you will need to provide a fake implementation of this method since\notherwise Elmer will not know how to handle the generated command.\n\nA port that receives data from Javascript works just the same as any subscription.\n\n```\nport receiveData : (String -\u003e msg) -\u003e Sub msg\n\ntype Msg = ReceivedData String\n\nsubscriptions : Module -\u003e Sub Msg\nsubscriptions model =\n  receiveData ReceivedData\n```\n\nWe can create a spy for this subscription-generating function and provide a\nfake subscription that will allow us to send data tagged with the appropriate message\nduring our test.\n\n```\nlet\n  spy =\n    Elmer.Spy.observe (\\_ -\u003e MyModule.receiveData)\n      |\u003e Elmer.Spy.andCallFake (\\tagger -\u003e\n           Elmer.Subscription.fake \"fake-receive\" tagger\n         )\nin\n  Elmer.given MyModule.defaultModel MyModule.view MyModule.update\n    |\u003e Elmer.Spy.use [ spy ]\n    |\u003e Elmer.Subscription.with (\\_ -\u003e MyModule.subscriptions)\n    |\u003e Elmer.Subscription.send \"fake-receive\" \"some fake data\"\n    |\u003e ...\n```\n\n### Testing Tasks\n\nElm uses tasks to describe asynchronous operations at a high-level. You can use Elmer\nto describe the behavior of applications that use the Task API. To do so:\n\n1. Stub any task-generating functions to return a task created with `Task.succeed`\nor `Task.fail` and the value you want as necessary for the behavior you want to describe.\n\n2. That's it.\n\nElmer does not know how to run any tasks other than `Task.succeed` and `Task.fail`.\nHowever, Elmer does know how to properly apply all the functions from the Task API.\nIn this way, Elmer allows you to describe the behavior that results from operations\nwith tasks without actually running those tasks during your test.\n\nHere's an example. Suppose when a button is clicked, your app creates a task that gets\nthe current time, formats it (using some function called `formatTime : Time -\u003e String`),\nand tags the resulting string with `TagFormattedTime` like so:\n\n    Time.now\n      |\u003e Task.map formatTime\n      |\u003e Task.perform TagFormattedTime\n\nYou can test this behavior by replacing `Time.now` with a `Task.succeed`\nthat resolves to the time you want.\n\n    let\n      timeSpy =\n        Task.succeed (Time.millisToPosix 1515281017615)\n          |\u003e Spy.replaceValue (\\_ -\u003e Time.now)\n    in\n      testState\n        |\u003e Elmer.Spy.use [ timeSpy ]\n        |\u003e Elmer.Html.target \u003c\u003c by [ id \"get-current-time\" ]\n        |\u003e Elmer.Html.Event.click\n        |\u003e Elmer.Html.target \u003c\u003c by [ id \"current-time\" ]\n        |\u003e Elmer.Html.expect (\n          element \u003c| hasText \"1/6/2018 23:23:37\"\n        )\n\nSee the `Elmer.Task` module for more tasks that are useful when writing tests or\nbuilding extensions to Elmer.\n\n\n### Spies and Fakes\n\nElmer generalizes the pattern for managing the effects of `Subs` and `Cmds`, allowing\nyou to spy on any function you like. *NOTE* You should use Elmer spies sparingly and\nwith care. Each spy that you add to your test couples that test to implementation details. \n\nSuppose you need to write a test that expects a certain function to be called, but\nyou don't need to describe the resulting behavior. You can spy on a function with\n`Elmer.Spy.observe` and make expectations about it with `Elmer.Spy.expect`.\n\nFor example, suppose you want to ensure that a component is calling a specific\nfunction in another module for parsing some string. You have tests for the\nparsing function itself; you just need to know that your component is using it.\n\n```\nparseTest : Test\nparseTest =\n  describe \"when the string is submitted\"\n  [ test \"it passes it to the parsing module\" \u003c|\n    \\() -\u003e\n      let\n        spy = \n          Elmer.Spy.observe (\\_ -\u003e MyParserModule.parse)\n            |\u003e Elmer.Spy.andCallThrough\n      in\n        Elmer.given App.defaultModel App.view App.update\n          |\u003e Elmer.Spy.use [ spy ]\n          |\u003e Elmer.Html.target \u003c\u003c by [ tag \"input\", attribute (\"type\", \"text\") ]\n          |\u003e Elmer.Html.Event.input \"A string to be parsed\"\n          |\u003e Elmer.Spy.expect (\\_ -\u003e MyParserModule.parse) (\n            wasCalled 1\n          )\n  ]\n```\n\nElmer also allows you to provide a fake implementation for any function.\nSuppose that you want to stub the result of the parsing function:\n\n```\nparseTest : Test\nparseTest =\n  describe \"when the string is submitted\"\n  [ test \"it displays the parsed result\" \u003c|\n    \\() -\u003e\n      let\n        spy =\n          Elmer.Spy.observe (\\_ -\u003e MyParserModule.parse)\n            |\u003e Elmer.Spy.andCallFake (\\_ -\u003e\n              \"Some Parsed String\"\n            )\n      in\n        Elmer.given App.defaultModel App.view App.update\n          |\u003e Elmer.Spy.use [ spy ]\n          |\u003e Elmer.Html.target \u003c\u003c by [ tag \"input\", attribute (\"type\", \"text\") ]\n          |\u003e Elmer.Html.Event.input \"A string to be parsed\"\n          |\u003e Elmer.Html.target \u003c\u003c by [ id \"parsing-result\" ]\n          |\u003e Elmer.Html.expect (Elmer.Html.element \u003c|\n            Elmer.Html.Matchers.hasText \"Some Parsed String\n          )\n  ]\n```\n\nFor any spy, you can make an expectation about how many times it was called like so:\n\n```\nElmer.Spy.expect (\\_ -\u003e MyModule.someFunction) (wasCalled 3)\n```\n\nYou can also expect that the spy was called with some list of arguments at least once:\n\n```\nElmer.Spy.expect (\\_ -\u003e MyModule.someFunction) (\n  Elmer.Spy.Matchers.wasCalledWith\n    [ Elmer.Spy.Matchers.stringArg \"someString\"\n    , Elmer.Spy.Matchers.anyArg\n    , Elmer.Spy.Matcher.intArg 23\n    ]\n)\n```\n\nSee `Elmer.Spy.Matchers` for a full list of argument matchers.\n\n`Elmer.Spy.observe` is good for spying on named functions in your production code.\nSometimes, though, it would be nice to provide the code you are testing with a 'fake' function\nfor testing purposes only. Suppose that you are testing a module that takes a function\nas an argument, and you want to expect that the function is called with a certain argument.\nYou can create a 'fake' function in your test module, observe it with a spy, and provide it to\nthe code you're testing using `Elmer.Spy.inject`. For example:\n\n```\nsomeFake tagger data =\n  Command.fake \u003c| tagger data\n\nmyTest =\n  let\n    spy =\n      Spy.observe (\\_ -\u003e someFake)\n        |\u003e Spy.andCallThrough\n  in\n    Elmer.given testModel MyModule.view (MyModule.updateUsing \u003c| Spy.inject (\\_ -\u003e someFake))\n      |\u003e Elmer.Spy.use [ spy ]\n      |\u003e Elmer.Html.target \u003c\u003c by [ tag \"input\" ]\n      |\u003e Elmer.Html.Event.input \"some text\"\n      |\u003e Elmer.Html.target \u003c\u003c by [ tag \"button\" ]\n      |\u003e Elmer.Html.Event.click\n      |\u003e Elmer.Spy.expect (\\_ -\u003e someFake) (\n        Elmer.Spy.Matchers.wasCalledWith\n          [ Elmer.Spy.Matchers.anyArg\n          , Elmer.Spy.Matchers.stringArg \"some text\"\n          ]\n      )\n```\n\nUse `Elmer.Spy.inject` to provide the function you want to observe so that Elmer has time to install the associated spy during the test.\n\nFinally, you can use `Spy.replaceValue` to replace the value returned by a no-argument function\n(such as `Time.now`) during a test. You can't make expectations about spies created in this\nway; `Spy.replaceValue` is just a convenient way to inject fake values during a test.\n\n\n### Extensions\n\nIt's easy to build extensions on top of Elmer to provide custom matchers or extra functions that\nhelp describe the behavior of an Elm application. In particular, `Elmer.Value`, `Elmer.Message`, \nand `Elmer.Effects` provide functions that are useful when writing custom matchers or extension\nmodules.\n\nFor a good example of an extension, see the [Elmer.Http](https://github.com/brian-watkins/elmer-http)\npackage, which adds support for describing the behavior of apps that use HTTP. \n\n\n### Upgrading from Elmer 5.x\n\nThe `Elmer.Http` api has been removed and moved to its own [package](https://github.com/brian-watkins/elmer-http).\n\n`Elmer.Command.resolveDeferred` has moved to `Elmer.resolveDeferred` to accomodate the fact that Tasks can\nalso be deferred. \n\n\n### Upgrading from Elmer 4.x\n\nIf you've written tests with Elmer 4.x, the `Elmer.Spy` api has changed:\n\n- `Elmer.Spy.create` is now `Elmer.Spy.observe` and no longer needs a string identifier.\n\n- When using `Elmer.Spy.observe` you must call either `Elmer.Spy.andCallThrough` or `Elmer.Spy.andCallFake` so\nthat Elmer knows what to do when the observed function is called.\n\n- `Elmer.Spy.expect` now uses a reference to the observed function (rather than a string) to identify the function\nyou'd like to assert about. \n\n- `Elmer.Spy.createWith` has been removed. Create a 'fake' function in your test module and observe it with `Elmer.Spy.observe` instead.\n\n- `Elmer.Spy.callable` has been removed. Instead, use `Elmer.Spy.inject` to provide a 'fake' function to the code under test. \n\n\n### Upgrading from Elmer 3.x\n\nIf you've written tests with Elmer 3.x and plan to upgrade them to Elmer 4.0.0, here are some things\nyou'll need to consider:\n\n- `Elmer.Platform.Command` has changed to `Elmer.Command`.\n\n- `Elmer.Platform.Subscription` has changed to `Elmer.Subscription`.\n\n- `Elmer.Headless.givenCommand` has been replaced with `Elmer.Command.given`.\n\n- `Elmer.Headless.expectMessages` has been replaced with `Elmer.Command.expectMessages`.\n\n- `Elmer.Headless.given` has been replaced with `Elmer.Program.givenWorker`.\n\n- `Elmer.Http.expectThat` has been replaced with `Elmer.Http.expect`.\n\n- `Elmer.Http.expect` has been replaced with `Elmer.Http.expectRequest`.\n\n- The `\u003c\u0026\u0026\u003e` operator has been replaced with `Elmer.expectAll`.\n\n- `Elmer.Html.Matchers.hasProperty` has been removed. Use `Elmer.Html.Matchers.hasAttribute` instead.\n\n- `Elmer.Html.target` now has a new api. Combine functions in the `Elmer.Html.Selector` module to build a an element selector.\n\n\n### Development\n\nTo run the tests:\n\n```\n$ cd tests\n$ ./test.sh\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrian-watkins%2Felmer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrian-watkins%2Felmer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrian-watkins%2Felmer/lists"}