www

My personal website(s)
Log | Files | Refs

2019-03-23-gotest-tools-poll.html (8770B)


      1 <!DOCTYPE html>
      2 <html lang="en">
      3 <head>
      4 <!-- Sep 03, 2024 -->
      5 <meta charset="utf-8" />
      6 <meta name="viewport" content="width=device-width, initial-scale=1" />
      7 <title>Golang testing — gotest.tools poll</title>
      8 <meta name="author" content="Vincent Demeester" />
      9 <meta name="generator" content="Org Mode" />
     10 <link rel='icon' type='image/x-icon' href='/images/favicon.ico'/>
     11 <meta name='viewport' content='width=device-width, initial-scale=1'>
     12 <link rel='stylesheet' href='/css/new.css' type='text/css'/>
     13 <link rel='stylesheet' href='/css/syntax.css' type='text/css'/>
     14 <link href='/index.xml' rel='alternate' type='application/rss+xml' title='Vincent Demeester' />
     15 </head>
     16 <body>
     17 <main id="content" class="content">
     18 <header>
     19 <h1 class="title">Golang testing — gotest.tools poll</h1>
     20 </header><nav id="table-of-contents" role="doc-toc">
     21 <h2>Table of Contents</h2>
     22 <div id="text-table-of-contents" role="doc-toc">
     23 <ul>
     24 <li><a href="#Introduction">Introduction</a></li>
     25 <li><a href="#~WaitOn~"><code>WaitOn</code></a></li>
     26 <li><a href="#~Check~%20and%20~Result~"><code>Check</code> and <code>Result</code></a></li>
     27 <li><a href="#Conclusion">Conclusion</a></li>
     28 </ul>
     29 </div>
     30 </nav>
     31 <section id="outline-container-Introduction" class="outline-2">
     32 <h2 id="Introduction">Introduction</h2>
     33 <div class="outline-text-2" id="text-Introduction">
     34 <p>
     35 Let&rsquo;s continue the <a href="https://gotest.tools"><code>gotest.tools</code></a> serie, this time with the <code>poll</code> package.
     36 </p>
     37 
     38 <blockquote>
     39 <p>
     40 Package poll provides tools for testing asynchronous code.
     41 </p>
     42 </blockquote>
     43 
     44 <p>
     45 When you write test, you may test a piece of code that work asynchronously, where the
     46 state you&rsquo;re expecting is gonna take a bit of time to be achieved. This is especially true
     47 when you work on networking or file-system code. And this happens a lot when you write
     48 integration (or end-to-end) test, less for unit-tests.
     49 </p>
     50 
     51 <p>
     52 The package <code>poll</code> is trying to tackle those use cases. We&rsquo;ll first take a look at the
     53 main function, <code>WaitOn</code>, then how to write a <code>Check</code>, using the <code>Result</code> type.
     54 </p>
     55 </div>
     56 </section>
     57 <section id="outline-container-~WaitOn~" class="outline-2">
     58 <h2 id="~WaitOn~"><code>WaitOn</code></h2>
     59 <div class="outline-text-2" id="text-~WaitOn~">
     60 <p>
     61 Let&rsquo;s look into the main <code>poll</code> function : `WaitOn`.
     62 </p>
     63 
     64 <blockquote>
     65 <p>
     66 WaitOn a condition or until a timeout. Poll by calling check and exit when check returns
     67 a done Result. To fail a test and exit polling with an error return a error result.
     68 </p>
     69 </blockquote>
     70 
     71 <p>
     72 In a gist, <code>WaitOn</code> will run a <i>condition</i> function until it either times out or
     73 succeed. It wait for a given time/delay between each run.
     74 </p>
     75 
     76 <div class="org-src-container">
     77 <pre class="src src-go">func WaitOn(t TestingT, check Check, pollOps ...SettingOp) {
     78         // […]
     79 }
     80 </pre>
     81 </div>
     82 
     83 <p>
     84 As any <i>testing helper</i> function, the first argument is <code>*testing.T</code> (or, in this case,
     85 any thing that look like it, thanks to the <code>TestingT</code> interace). The two other arguments
     86 are way more interesting :
     87 </p>
     88 
     89 <ul class="org-ul">
     90 <li>The <code>Check</code> is the condition that will run multiple times until it either timeout, or succeed.</li>
     91 <li>The <code>SettingOp(s)</code> which are options to configure the function, things like the timeout,
     92 or the <i>delay</i> between each run.</li>
     93 </ul>
     94 
     95 <p>
     96 The settings are pretty straightforward :
     97 </p>
     98 
     99 <ul class="org-ul">
    100 <li><code>WithDelay</code> : sets the delay to wait between polls. The default delay is 100ms.</li>
    101 <li><code>WithTimeout</code> : sets the timeout. The default timeout is 10s.</li>
    102 </ul>
    103 
    104 <p>
    105 There is existing <code>Check</code> for common case:
    106 </p>
    107 
    108 <ul class="org-ul">
    109 <li><p>
    110 <code>Connection</code> : try to open a connection to the address on the named network.
    111 </p>
    112 
    113 <div class="org-src-container">
    114 <pre class="src src-go">poll.WaitOn(t, poll.Connection("tcp", "foo.bar:55555"), poll.WithTimeout("5s"))
    115 </pre>
    116 </div></li>
    117 
    118 <li><p>
    119 <code>FileExists</code> : looks on filesystem and check that path exists.
    120 </p>
    121 
    122 <div class="org-src-container">
    123 <pre class="src src-go">poll.WaitOn(t, poll.FileExists("/should/be/created"), poll.WithDelay("1s"))
    124 </pre>
    125 </div></li>
    126 </ul>
    127 </div>
    128 </section>
    129 <section id="outline-container-~Check~%20and%20~Result~" class="outline-2">
    130 <h2 id="~Check~%20and%20~Result~"><code>Check</code> and <code>Result</code></h2>
    131 <div class="outline-text-2" id="text-~Check~%20and%20~Result~">
    132 <p>
    133 <code>Connection</code> and <code>FileExists</code> are the only two <i>built-in</i> <code>Check</code> provided by
    134 <code>gotest.tools</code>. They are useful, but as usual, where <code>gotest.tools</code> shines is
    135 extensiblity. It is really easy to define your own <code>Check</code>.
    136 </p>
    137 
    138 <div class="org-src-container">
    139 <pre class="src src-go">type Check func(t LogT) Result
    140 </pre>
    141 </div>
    142 
    143 <p>
    144 A <code>Check</code> is, thus, only a function that takes <code>LogT</code> — which is anything that can log
    145 something, like <code>*testing.T</code> — and return a <code>Result</code>. Let&rsquo;s look at this intersting
    146 <code>Result</code> type.
    147 </p>
    148 
    149 <div class="org-src-container">
    150 <pre class="src src-go">type Result interface {
    151     // Error indicates that the check failed and polling should stop, and the
    152     // the has failed
    153     Error() error
    154     // Done indicates that polling should stop, and the test should proceed
    155     Done() bool
    156     // Message provides the most recent state when polling has not completed
    157     Message() string
    158 }
    159 </pre>
    160 </div>
    161 
    162 <p>
    163 Although it&rsquo;s an interface, the <code>poll</code> package defines built-in <code>Result</code> so that it&rsquo;s easy
    164 to write <code>Check</code> without having to define you <code>Result</code> type.
    165 </p>
    166 
    167 <ul class="org-ul">
    168 <li><code>Continue</code> returns a Result that indicates to WaitOn that it should continue
    169 polling. The message text will be used as the failure message if the timeout is reached.</li>
    170 <li><code>Success</code> returns a Result where Done() returns true, which indicates to WaitOn that it
    171 should stop polling and exit without an error.</li>
    172 <li><code>Error</code> returns a Result that indicates to WaitOn that it should fail the test and stop
    173 polling.</li>
    174 </ul>
    175 
    176 <p>
    177 The basic just to write a <code>Check</code> is then :
    178 </p>
    179 
    180 <ul class="org-ul">
    181 <li>if the state is not there yet, return <code>Continue</code>,</li>
    182 <li>if there is an error, unrelated to validating the state, return an <code>Error</code>,</li>
    183 <li>if the state is there, return <code>Success</code>.</li>
    184 </ul>
    185 
    186 <p>
    187 Let&rsquo;s look at an example taken from the <code>moby/moby</code> source code.
    188 </p>
    189 
    190 <div class="org-src-container">
    191 <pre class="src src-go">poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
    192 
    193 func IsInState(ctx context.Context, client client.APIClient, containerID string, state ...string) func(log poll.LogT) poll.Result {
    194         return func(log poll.LogT) poll.Result {
    195                 inspect, err := client.ContainerInspect(ctx, containerID)
    196                 if err != nil {
    197                         return poll.Error(err)
    198                 }
    199                 for _, v := range state {
    200                         if inspect.State.Status == v {
    201                                 return poll.Success()
    202                         }
    203                 }
    204                 return poll.Continue("waiting for container to be one of (%s), currently %s", strings.Join(state, ", "), inspect.State.Status)
    205         }
    206 }
    207 </pre>
    208 </div>
    209 </div>
    210 </section>
    211 <section id="outline-container-Conclusion" class="outline-2">
    212 <h2 id="Conclusion">Conclusion</h2>
    213 <div class="outline-text-2" id="text-Conclusion">
    214 <p>
    215 … that&rsquo;s a wrap. The <code>poll</code> package allows to easily wait for a condition to happen in a
    216 given time-frame — with sane defaults. As for most of the <code>gotest.tools</code> package, we use
    217 this package heavily in <code>docker/*</code> projects too…
    218 </p>
    219 </div>
    220 </section>
    221 </main>
    222 <footer id="postamble" class="status">
    223 <footer>
    224      <small><a href="/" rel="history">Index</a> • <a href="/sitemap.html">Sitemap</a> • <a href="https://dl.sbr.pm/">Files</a></small><br/>
    225      <small class='questions'>Questions, comments ? Please use my <a href="https://lists.sr.ht/~vdemeester/public-inbox">public inbox</a> by sending a plain-text email to <a href="mailto:~vdemeester/public-inbox@lists.sr.ht">~vdemeester/public-inbox@lists.sr.ht</a>.</small><br/>
    226      <small 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     </small><br />
    230 </footer>
    231 </footer>
    232 </body>
    233 </html>