home

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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&rsquo;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&rsquo;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&rsquo;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&rsquo;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">&#34;tcp&#34;</span><span class="p">,</span> <span class="s">&#34;foo.bar:55555&#34;</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">&#34;5s&#34;</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">&#34;/should/be/created&#34;</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">&#34;1s&#34;</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&rsquo;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&rsquo;s an interface, the <code>poll</code> package defines built-in <code>Result</code> so that it&rsquo;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&rsquo;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">&#34;running&#34;</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">&#34;waiting for container to be one of (%s), currently %s&#34;</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">&#34;, &#34;</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&rsquo;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