index.html (14846B)
1 <!DOCTYPE html> 2 3 <html lang="en"> 4 5 <head> 6 <meta charset="utf-8"> 7 <meta http-equiv="content-type" content="text/html; charset=utf-8" /> 8 9 <link rel="start" href="https://vincent.demeester.fr" /> 10 11 <title>Vincent Demeester</title> 12 <link rel="canonical" href="https://vincent.demeester.fr/posts/2019-03-23-gotest-tools-poll/"> 13 <link href="https://vincent.demeester.fr/index.xml" rel="alternate" type="application/rss+xml" title="Vincent Demeester" /> 14 15 <link rel="openid.server" href="https://indieauth.com/openid" /> 16 <link rel="openid.delegate" href="http://vincent.demeester.fr/" /> 17 <link rel="shortcut icon" type="image/x-icon" href="favicon.ico"> 18 19 <link rel="stylesheet" href="/css/screen.css" type="text/css" /> 20 <link rel="stylesheet" href="/css/sbrain.css" type="text/css" /> 21 <link rel="stylesheet" href="/css/syntax.css" type="text/css" /> 22 23 </head> 24 25 <body lang=""/> 26 27 28 29 30 31 32 <div id="main-container"> 33 <div id="page"> 34 <article class="post"> 35 <header> 36 <h1 class="emphnext">Golang testing — gotest.tools poll</h1><a href='https://vincent.demeester.fr/posts/2019-03-23-gotest-tools-poll/'></a> 37 <address class="signature"> 38 <span class="date">Sat, 23 March, 2019</span> 39 <span class="words">(700 Words)</span> 40 </address> 41 <ul class="tag_box inline"> 42 43 <li class="category"><a href="/categories/#developement">developement</a></li> 44 45 46 47 48 49 <li class="tag tag-testing"><a href="/tags/#testing">testing<span>11</span></a></li> 50 51 52 <li class="tag tag-golang"><a href="/tags/#golang">golang<span>12</span></a></li> 53 54 55 <li class="tag tag-poll"><a href="/tags/#poll">poll<span>1</span></a></li> 56 57 <br/> 58 59 </ul> 60 </header> 61 62 63 64 65 66 <p>Let’s continue the <a href="https://gotest.tools"><code>gotest.tools</code></a> serie, this time with the <code>poll</code> package.</p> 67 68 <blockquote> 69 <p>Package poll provides tools for testing asynchronous code.</p> 70 </blockquote> 71 72 <p>When you write test, you may test a piece of code that work asynchronously, where the 73 state you’re expecting is gonna take a bit of time to be achieved. This is especially true 74 when you work on networking or file-system code. And this happens a lot when you write 75 integration (or end-to-end) test, less for unit-tests.</p> 76 77 <p>The package <code>poll</code> is trying to tackle those use cases. We’ll first take a look at the 78 main function, <code>WaitOn</code>, then how to write a <code>Check</code>, using the <code>Result</code> type.</p> 79 80 <h2 id="waiton"><code>WaitOn</code></h2> 81 82 <p>Let’s look into the main <code>poll</code> function : `WaitOn`.</p> 83 84 <blockquote> 85 <p>WaitOn a condition or until a timeout. Poll by calling check and exit when check returns 86 a done Result. To fail a test and exit polling with an error return a error result.</p> 87 </blockquote> 88 89 <p>In a gist, <code>WaitOn</code> will run a <em>condition</em> function until it either times out or 90 succeed. It wait for a given time/delay between each run.</p> 91 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">WaitOn</span><span class="p">(</span><span class="nx">t</span> <span class="nx">TestingT</span><span class="p">,</span> <span class="nx">check</span> <span class="nx">Check</span><span class="p">,</span> <span class="nx">pollOps</span> <span class="o">...</span><span class="nx">SettingOp</span><span class="p">)</span> <span class="p">{</span> 92 <span class="c1">// […] 93 </span><span class="c1"></span><span class="p">}</span></code></pre></div> 94 <p>As any <em>testing helper</em> function, the first argument is <code>*testing.T</code> (or, in this case, 95 any thing that look like it, thanks to the <code>TestingT</code> interace). The two other arguments 96 are way more interesting :</p> 97 98 <ul> 99 <li>The <code>Check</code> is the condition that will run multiple times until it either timeout, or succeed.</li> 100 <li>The <code>SettingOp(s)</code> which are options to configure the function, things like the timeout, 101 or the <em>delay</em> between each run.</li> 102 </ul> 103 104 <p>The settings are pretty straightforward :</p> 105 106 <ul> 107 <li><code>WithDelay</code> : sets the delay to wait between polls. The default delay is 100ms.</li> 108 <li><code>WithTimeout</code> : sets the timeout. The default timeout is 10s.</li> 109 </ul> 110 111 <p>There is existing <code>Check</code> for common case:</p> 112 113 <ul> 114 <li><p><code>Connection</code> : try to open a connection to the address on the named network.</p> 115 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="nx">poll</span><span class="p">.</span><span class="nf">WaitOn</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">poll</span><span class="p">.</span><span class="nf">Connection</span><span class="p">(</span><span class="s">"tcp"</span><span class="p">,</span> <span class="s">"foo.bar:55555"</span><span class="p">),</span> <span class="nx">poll</span><span class="p">.</span><span class="nf">WithTimeout</span><span class="p">(</span><span class="s">"5s"</span><span class="p">))</span></code></pre></div></li> 116 117 <li><p><code>FileExists</code> : looks on filesystem and check that path exists.</p> 118 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="nx">poll</span><span class="p">.</span><span class="nf">WaitOn</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">poll</span><span class="p">.</span><span class="nf">FileExists</span><span class="p">(</span><span class="s">"/should/be/created"</span><span class="p">),</span> <span class="nx">poll</span><span class="p">.</span><span class="nf">WithDelay</span><span class="p">(</span><span class="s">"1s"</span><span class="p">))</span></code></pre></div></li> 119 </ul> 120 121 <h2 id="check-and-result"><code>Check</code> and <code>Result</code></h2> 122 123 <p><code>Connection</code> and <code>FileExists</code> are the only two <em>built-in</em> <code>Check</code> provided by 124 <code>gotest.tools</code>. They are useful, but as usual, where <code>gotest.tools</code> shines is 125 extensiblity. It is really easy to define your own <code>Check</code>.</p> 126 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Check</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="nx">LogT</span><span class="p">)</span> <span class="nx">Result</span></code></pre></div> 127 <p>A <code>Check</code> is, thus, only a function that takes <code>LogT</code> — which is anything that can log 128 something, like <code>*testing.T</code> — and return a <code>Result</code>. Let’s look at this intersting 129 <code>Result</code> type.</p> 130 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Result</span> <span class="kd">interface</span> <span class="p">{</span> 131 <span class="c1">// Error indicates that the check failed and polling should stop, and the 132 </span><span class="c1"></span> <span class="c1">// the has failed 133 </span><span class="c1"></span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">error</span> 134 <span class="c1">// Done indicates that polling should stop, and the test should proceed 135 </span><span class="c1"></span> <span class="nf">Done</span><span class="p">()</span> <span class="kt">bool</span> 136 <span class="c1">// Message provides the most recent state when polling has not completed 137 </span><span class="c1"></span> <span class="nf">Message</span><span class="p">()</span> <span class="kt">string</span> 138 <span class="p">}</span></code></pre></div> 139 <p>Although it’s an interface, the <code>poll</code> package defines built-in <code>Result</code> so that it’s easy 140 to write <code>Check</code> without having to define you <code>Result</code> type.</p> 141 142 <ul> 143 <li><code>Continue</code> returns a Result that indicates to WaitOn that it should continue 144 polling. The message text will be used as the failure message if the timeout is reached.</li> 145 <li><code>Success</code> returns a Result where Done() returns true, which indicates to WaitOn that it 146 should stop polling and exit without an error.</li> 147 <li><code>Error</code> returns a Result that indicates to WaitOn that it should fail the test and stop 148 polling.</li> 149 </ul> 150 151 <p>The basic just to write a <code>Check</code> is then :</p> 152 153 <ul> 154 <li>if the state is not there yet, return <code>Continue</code>,</li> 155 <li>if there is an error, unrelated to validating the state, return an <code>Error</code>,</li> 156 <li>if the state is there, return <code>Success</code>.</li> 157 </ul> 158 159 <p>Let’s look at an example taken from the <code>moby/moby</code> source code.</p> 160 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="nx">poll</span><span class="p">.</span><span class="nf">WaitOn</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">container</span><span class="p">.</span><span class="nf">IsInState</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">client</span><span class="p">,</span> <span class="nx">cID</span><span class="p">,</span> <span class="s">"running"</span><span class="p">),</span> <span class="nx">poll</span><span class="p">.</span><span class="nf">WithDelay</span><span class="p">(</span><span class="mi">100</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="p">))</span> 161 162 <span class="kd">func</span> <span class="nf">IsInState</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">client</span> <span class="nx">client</span><span class="p">.</span><span class="nx">APIClient</span><span class="p">,</span> <span class="nx">containerID</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">state</span> <span class="o">...</span><span class="kt">string</span><span class="p">)</span> <span class="kd">func</span><span class="p">(</span><span class="nx">log</span> <span class="nx">poll</span><span class="p">.</span><span class="nx">LogT</span><span class="p">)</span> <span class="nx">poll</span><span class="p">.</span><span class="nx">Result</span> <span class="p">{</span> 163 <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">log</span> <span class="nx">poll</span><span class="p">.</span><span class="nx">LogT</span><span class="p">)</span> <span class="nx">poll</span><span class="p">.</span><span class="nx">Result</span> <span class="p">{</span> 164 <span class="nx">inspect</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">ContainerInspect</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">containerID</span><span class="p">)</span> 165 <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> 166 <span class="k">return</span> <span class="nx">poll</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> 167 <span class="p">}</span> 168 <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">state</span> <span class="p">{</span> 169 <span class="k">if</span> <span class="nx">inspect</span><span class="p">.</span><span class="nx">State</span><span class="p">.</span><span class="nx">Status</span> <span class="o">==</span> <span class="nx">v</span> <span class="p">{</span> 170 <span class="k">return</span> <span class="nx">poll</span><span class="p">.</span><span class="nf">Success</span><span class="p">()</span> 171 <span class="p">}</span> 172 <span class="p">}</span> 173 <span class="k">return</span> <span class="nx">poll</span><span class="p">.</span><span class="nf">Continue</span><span class="p">(</span><span class="s">"waiting for container to be one of (%s), currently %s"</span><span class="p">,</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="nx">state</span><span class="p">,</span> <span class="s">", "</span><span class="p">),</span> <span class="nx">inspect</span><span class="p">.</span><span class="nx">State</span><span class="p">.</span><span class="nx">Status</span><span class="p">)</span> 174 <span class="p">}</span> 175 <span class="p">}</span></code></pre></div> 176 <h2 id="conclusion">Conclusion</h2> 177 178 <p>… that’s a wrap. The <code>poll</code> package allows to easily wait for a condition to happen in a 179 given time-frame — with sane defaults. As for most of the <code>gotest.tools</code> package, we use 180 this package heavily in <code>docker/*</code> projects too…</p> 181 182 183 </article> 184 <hr /> 185 <div class="prev-next"> 186 187 <a class="paging-link prev" href="/posts/2019-03-24-link/" title="Building Blocks – I.A.">← Previous post</a> 188 189 190 191 <a class="paging-link next" href="/posts/2019-03-12-link/" title="Go2 Contracts Go Too Far · npf.io">Next post →</a> 192 193 </div> 194 195 </div> 196 </div> 197 198 <footer> 199 <nav> 200 201 <a href="/">home</a> 202 <span class="text-muted"> | </span> 203 204 <a href="/about">about</a> 205 <span class="text-muted"> | </span> 206 207 <a href="/archive">archive</a> 208 <span class="text-muted"> | </span> 209 210 <a href="/categories">categories</a> 211 <span class="text-muted"> | </span> 212 213 <a href="/tags">tags</a> 214 <span class="text-muted"> | </span> 215 216 <a href="https://twitter.com/vdemeest">twitter</a> 217 <span class="text-muted"> | </span> 218 219 <a href="https://github.com/vdemeester">github</a> 220 <span class="text-muted"> | </span> 221 222 <a href="https://vincent.demeester.fr/index.xml">rss</a> 223 </nav> 224 <br/> 225 <address> 226 <span class="copyright"> 227 Content and design by Vincent Demeester 228 (<a rel="licence" href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Some rights reserved</a>) 229 </span><br /> 230 <span class="engine"> 231 Powered by <a href="https://gohugo.io/">Hugo</a> and <a href="https://github.com/kaushalmodi/ox-hugo/">ox-hugo</a> 232 </span> 233 </address> 234 </footer> 235 </body> 236