1<!DOCTYPE html> 2<html lang="en-US"> 3 <head> 4 <meta charset="utf-8"> 5<!-- This site is hosted on Netlify. Anyone can build and deploy a site 6 like this one for free: https://netlify.new/?utm_campaign=loops&utm_source=ai-legible&utm_medium=owned&utm_content=comment&utm_id=ba90cc5e-81e2-40b5-b457-107522fd3634 7 Netlify hosting facts for this site: static/SSR served via Netlify Edge. --> 8 <meta name="viewport" content="width=device-width,initial-scale=1"> 9 <title>Testing programs with ports | avh4/elm-program-test</title> 10 <meta name="generator" content="VuePress 1.9.7"> 11 12 <meta name="description" content=""> 13 14 <link rel="preload" href="/assets/css/0.styles.76626958.css" as="style"><link rel="preload" href="/assets/js/app.667806f6.js" as="script"><link rel="preload" href="/assets/js/2.1d215991.js" as="script"><link rel="preload" href="/assets/js/8.f5193335.js" as="script"><link rel="prefetch" href="/assets/js/10.7e37fa33.js"><link rel="prefetch" href="/assets/js/11.47227962.js"><link rel="prefetch" href="/assets/js/3.3b1cbf3d.js"><link rel="prefetch" href="/assets/js/4.b2dec8d5.js"><link rel="prefetch" href="/assets/js/5.2b733c3b.js"><link rel="prefetch" href="/assets/js/6.feea877e.js"><link rel="prefetch" href="/assets/js/7.96fac09b.js"><link rel="prefetch" href="/assets/js/9.277674e8.js"> 15 <link rel="stylesheet" href="/assets/css/0.styles.76626958.css"> 16 </head> 17 <body> 18 <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">avh4/elm-program-test</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/" class="nav-link"> 19 Guidebooks 20</a></div><div class="nav-item"><a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest" target="_blank" rel="noopener noreferrer" class="nav-link external"> 21 API Documentation 22 <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <a href="https://github.com/avh4/elm-program-test" target="_blank" rel="noopener noreferrer" class="repo-link"> 23 GitHub 24 <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/" class="nav-link"> 25 Guidebooks 26</a></div><div class="nav-item"><a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest" target="_blank" rel="noopener noreferrer" class="nav-link external"> 27 API Documentation 28 <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound">
28<path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <a href="https://github.com/avh4/elm-program-test" target="_blank" rel="noopener noreferrer" class="repo-link"> 29 GitHub 30 <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav> <ul class="sidebar-links"><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>Testing programs with ports</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a class='sidebar-link' href='/ports#introducing-the-example-program'>Introducing the example program</a><ul class="sidebar-sub-headers"></ul></li><li><a class='sidebar-link' href='/ports#goal-the-ideal-test'>Goal: the ideal test</a><ul class="sidebar-sub-headers"></ul></li><li><a class='sidebar-link' href='/ports#simulating-outgoing-ports'>Simulating outgoing ports</a><ul class="sidebar-sub-headers"></ul></li><li><a class='sidebar-link' href='/ports#simulating-incoming-ports'>Simulating incoming ports</a><ul class="sidebar-sub-headers"></ul></li><li><a class='sidebar-link' href='/ports#try-it-out'>Try it out</a><ul class="sidebar-sub-headers"></ul></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="testing-programs-with-ports"><a href="#testing-programs-with-ports" class="header-anchor">#</a> Testing programs with ports</h1> <p>This guide <strong>requires that you're already familiar with 31<a href='/cmds'>testing programs that use Cmds</a></strong>. 32Specifically, you should understand how to set up your program for testing 33by creating a custom Effect type and implementing a <code>simulateEffects</code> function. 34If you aren't yet familiar with doing that, you should follow the 35<a href='/cmds'>"testing programs that use Cmds" guide</a>
35 first and return to this guide afterwards.</p> <h2 id="introducing-the-example-program"><a href="#introducing-the-example-program" class="header-anchor">#</a> Introducing the example program</h2> <p>The rest of this section will be working with an example program 36that interacts with the fictional "GudWerdz" service for 37checking the grammar and writing style of a text input.</p> <p>The fictional "GudWerdz" service only provides a JavaScript SDK, 38so our app needs to interact with it using <a href="https://guide.elm-lang.org/interop/ports.html" target="_blank" rel="noopener noreferrer">ports<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>.</p> <p><img src="/assets/img/GrammarCheckExample.35f1773e.svg" alt="architecture diagram described in the preceding paragraph"></p> <h2 id="goal-the-ideal-test"><a href="#goal-the-ideal-test" class="header-anchor">#</a> Goal: the ideal test</h2> <p>The test we want to write represents the following scenario:</p> <ol><li>The user fills in the text area with some text.</li> <li>The user clicks the "Check" button.</li> <li>The app sends the text to check out through the <code>checkGrammar</code> port, which will call the GudWerdz API.</li> <li>The GudWerdz API returns a list of problems with the text, which get sent to the <code>checkGrammarResults</code> port.</li> <li>The app receives the results and displays them to the user.</li></ol> <p>Here's what that test will look like in code:</p> <div class="language-elm extra-class"><div class="highlight-lines"><br><br><br><br><br><br><br><br><br><br><br><br><br><div class="highlighted">Â </div><div class="highlighted">Â </div><div class="highlighted">Â </div><div class="highlighted">Â </div><div class="highlighted">Â </div><div class="highlighted">Â </div><div class="highlighted">Â </div><div class="highlighted">Â </div><div class="highlighted">Â </div><br><br><br></div><pre class="language-elm"><code><span class="token import-statement"><span class="token keyword">import</span> Expect</span> 39<span class="token import-statement"><span class="token keyword">import</span> Json.Decode</span> 40<span class="token import-statement"><span class="token keyword">import</span> Json.Encode</span> 41<span class="token import-statement"><span class="token keyword">import</span> ProgramTest</span> 42<span class="token import-statement"><span class="token keyword">import</span> Test <span class="token keyword">exposing</span> </span><span class="token punctuation">(</span><span class="token hvariable">test</span><span class="token punctuation">)</span> 43 44<span class="token hvariable">test</span> <span class="token string">"checking grammar"</span> <span class="token operator"><|</span> 45 \<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> 46 <span class="token hvariable">start</span> 47 <span class="token operator">|></span> <span class="token hvariable">ProgramTest.fillIn</span> <span class="token string">"main"</span> 48 <span class="token string">"Enter text to check"</span> 49 <span class="token string">"The youngest man the boat."</span> 50 <span class="token operator">|></span> <span class="token hvariable">ProgramTest.clickButton</span> <span class="token string">"Check"</span> 51 <span class="token operator">|></span> <span class="token hvariable">ProgramTest.ensureOutgoingPortValues</span> 52 <span class="token string">"checkGrammar"</span>
53 <span class="token hvariable">Json.Decode.string</span> 54 <span class="token punctuation">(</span><span class="token hvariable">Expect.equal</span> <span class="token punctuation">[</span> <span class="token string">"The youngest man the boat."</span> <span class="token punctuation">]</span><span class="token punctuation">)</span> 55 <span class="token operator">|></span> <span class="token hvariable">ProgramTest.simulateIncomingPort</span> 56 <span class="token string">"grammarCheckResults"</span> 57 <span class="token punctuation">(</span><span class="token hvariable">Json.Encode.list</span> <span class="token hvariable">Json.Encode.string</span> 58 <span class="token punctuation">[</span> <span class="token string">"Garden-path sentences can confuse the reader."</span> <span class="token punctuation">]</span> 59 <span class="token punctuation">)</span> 60 <span class="token operator">|></span> <span class="token hvariable">ProgramTest.expectViewHas</span> 61 <span class="token punctuation">[</span> <span class="token hvariable">text</span> <span class="token string">"Garden-path sentences can confuse the reader."</span> <span class="token punctuation">]</span> 62</code></pre></div><p>After entering some text and clicking the "Check" button, 63the test uses <code>ensureOutgoingPortValues</code> to assert that the entered text has been sent out on the "checkGrammar" port. 64Then <code>simulateIncomingPort</code> is used to simulate a response coming in on the "grammarCheckResults" port.</p> <p>However, before this test can pass we'll have to do some setup work 65so that the test can simulate the incoming and outgoing ports.</p> <h2 id="simulating-outgoing-ports"><a href="#simulating-outgoing-ports" class="header-anchor">#</a> Simulating outgoing ports</h2> <p>We'll assume that we already have our test set up with <code>ProgramTest.withSimulatedEffects</code> 66(if you're not familiar with that, 67you should first follow the <a href='/cmds'>"testing programs that use Cmds" guide</a>).</p> <p>To add simulation of our port effect, we'll make use of <code>SimulatedEffect.Ports.send</code> 68in our <code>simulateEffects</code> function:</p> <div class="language-elm extra-class"><pre class="language-elm"><code><span class="token import-statement"><span class="token keyword">import</span> Json.Encode</span> 69<span class="token import-statement"><span class="token keyword">import</span> SimulatedEffect.Ports</span> 70 71<span class="token hvariable">simulateEffects</span> <span class="token operator">:</span> <span class="token constant">Main.Effect</span> <span class="token operator">-></span> <span class="token constant">ProgramTest.SimulatedEffect</span> <span class="token constant">Main.Msg</span> 72<span class="token hvariable">simulateEffects</span> <span class="token hvariable">effect</span> <span class="token operator">=</span> 73 <span class="token keyword">case</span> <span class="token hvariable">effect</span> <span class="token keyword">of</span> 74 <span class="token operator">...</span> 75 <span class="token constant">Main.CheckGrammarEffect</span> <span class="token hvariable">text</span> <span class="token operator">-></span> 76 <span class="token hvariable">SimulatedEffect.Ports.send</span> <span class="token string">"checkGrammar"</span> <span class="token punctuation">(</span><span class="token hvariable">Json.Encode.string</span> <span class="token hvariable">text</span><span class="token punctuation">)</span> 77</code></pre></div><h2 id="simulating-incoming-ports"><a href="#simulating-incoming-ports" class="header-anchor">#</a> Simulating incoming ports</h2> <p>Similar to how we provided a <code>simulateEffects</code> function for testing our Cmds, 78we'll need to provide a <code>simulateSubscriptions</code> function for testing our Subs. 79This will parallel the <code>subscriptions</code> function in our main program, 80but will return <code>ProgramTest.SimulatedSub</code> instead of the normal <code>Sub</code>. 81(This is necessary because, as with <code>Cmd</code>s, 82Elm currently provides no way to inspect the value of a <code>Sub</code>.)</p> <p>For reference, here's our program's <code>subscriptions</code> function:</p> <div class="language-elm extra-class"><pre class="language-elm"><code><span class="token hvariable">port</span> <span class="token hvariable">grammarCheckResults</span> <span class="token operator">:</span> <span class="token punctuation">(</span><span class="token constant">List</span> <span class="token constant">String</span> <span class="token operator">-></span> <span class="token hvariable">msg</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token constant">Sub</span> <span class="token hvariable">msg</span> 83 84<span class="token hvariable">subscriptions</span> <span class="token operator">:</span> <span class="token constant">Model</span> <span class="token operator">-></span> <span class="token constant">Sub</span> <span class="token constant">Msg</span> 85<span class="token hvariable">subscriptions</span> _ <span class="token operator">=</span> 86 <span class="token hvariable">grammarCheckResults</span> <span class="token constant">GrammarCheckResults</span> 87</code></pre></div><p>And here's the corresponding <code>simulateSubscriptions</code> function we'll write for our test setup:</p> <div class="language-elm extra-class"><pre class="language-elm"><code><span class="token hvariable">simulateSubscriptions</span> <span class="token operator">:</span> <span class="token constant">
87Main.Model</span> <span class="token operator">-></span> <span class="token constant">ProgramTest.SimulatedSub</span> <span class="token constant">Main.Msg</span> 88<span class="token hvariable">simulateSubscriptions</span> _ <span class="token operator">=</span> 89 <span class="token hvariable">SimulatedEffect.Ports.subscribe</span> <span class="token string">"grammarCheckResults"</span> 90 <span class="token punctuation">(</span><span class="token hvariable">Json.Decode.list</span> <span class="token hvariable">Json.Decode.string</span><span class="token punctuation">)</span> 91 <span class="token constant">Main.GrammarCheckResults</span> 92</code></pre></div><p>Finally, we configure our <code>ProgramTest</code> with the <code>simulateSubscriptions</code> function before starting it:</p> <div class="language-elm extra-class"><div class="highlight-lines"><br><br><br><br><br><br><br><br><div class="highlighted">Â </div><br><br></div><pre class="language-elm"><code><span class="token hvariable">start</span> <span class="token operator">:</span> <span class="token constant">ProgramTest</span> <span class="token constant">Main.Msg</span> <span class="token constant">Main.Model</span> <span class="token constant">Main.Effect</span> 93<span class="token hvariable">start</span> <span class="token operator">=</span> 94 <span class="token hvariable">ProgramTest.createDocument</span> 95 <span class="token punctuation">{</span> <span class="token hvariable">init</span> <span class="token operator">=</span> <span class="token hvariable">Main.init</span> 96 <span class="token punctuation">,</span> <span class="token hvariable">update</span> <span class="token operator">=</span> <span class="token hvariable">Main.update</span> 97 <span class="token punctuation">,</span> <span class="token hvariable">view</span> <span class="token operator">=</span> <span class="token hvariable">Main.view</span> 98 <span class="token punctuation">}</span> 99 <span class="token operator">|></span> <span class="token hvariable">ProgramTest.withSimulatedEffects</span> <span class="token hvariable">simulateEffects</span> 100 <span class="token operator">|></span> <span class="token hvariable">ProgramTest.withSimulatedSubscriptions</span> <span class="token hvariable">simulateSubscriptions</span> 101 <span class="token operator">|></span> <span class="token hvariable">ProgramTest.start</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> 102</code></pre></div><h2 id="try-it-out"><a href="#try-it-out" class="header-anchor">#</a> Try it out</h2> <p>You can play around with the example here: 103<a href="https://ellie-app.com/6wskFByvrPta1" target="_blank" rel="noopener noreferrer">https://ellie-app.com/6wskFByvrPta1<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 104Try editing the tests or the implementation to make the tests fail 105and see what kind of failure messages you get.</p></div> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/avh4/elm-program-test/edit/main/docs/ports.md" target="_blank" rel="noopener noreferrer">Suggest changes to this page</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev"> 106 â 107 <a class='prev' href='/cmds'> 108 Testing programs with Cmds 109 </a></span> <!----></p></div> </main></div><div class="global-ui"></div></div> 110
110<script src="/assets/js/app.667806f6.js" defer></script>
110<script src="/assets/js/2.1d215991.js" defer></script>
110<script src="/assets/js/8.f5193335.js" defer></script>
110 111 </body> 112</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.