index.html (39989B)
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/2018-08-16-gotest-tools-assertions/"> 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 assertions</h1><a href='https://vincent.demeester.fr/posts/2018-08-16-gotest-tools-assertions/'></a> 37 <address class="signature"> 38 <span class="date">Thu, 16 August, 2018</span> 39 <span class="words">(2000 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-assert"><a href="/tags/#assert">assert<span>1</span></a></li> 56 57 <br/> 58 59 </ul> 60 </header> 61 62 <p>Faster way to send the same command to each and every <em>pane</em> in your 63 tmux <em>session</em>.</p> 64 65 <p>Let’s take a closer look at <a href="https://gotest.tools"><code>gotest.tools</code></a> assertions packages. This is mainly about <code>assert</code>, <code>assert/cmp</code> and 66 <code>assert/opt</code>.</p> 67 68 <blockquote> 69 <p>Package assert provides assertions for comparing expected values to actual values. When assertion fails a helpful error 70 message is printed.</p> 71 </blockquote> 72 73 <p>There is two main functions (<code>Assert</code> and <code>Check</code>) and some helpers (like <code>NilError</code>, …). They all take a <code>*testing.T</code> as 74 a first argument, pretty common across testing Go libraries. Let’s dive into those !</p> 75 76 77 78 79 <div class="ox-hugo-toc toc"> 80 <div></div> 81 82 <div class="heading">Table of Contents</div> 83 84 <ul> 85 <li><a href="#assert-and-check"><code>Assert</code> and <code>Check</code></a></li> 86 <li><a href="#more-assert-helpers">More <code>assert</code> helpers</a></li> 87 <li><a href="#cmp-dot-comparison"><code>cmp.Comparison</code></a> 88 89 <ul> 90 <li><a href="#equality-with-equal-and-deepequal">Equality with <code>Equal</code> and <code>DeepEqual</code></a></li> 91 <li><a href="#errors-with-error-errorcontains-and-errortype">Errors with <code>Error</code>, <code>ErrorContains</code> and <code>ErrorType</code></a></li> 92 <li><a href="#bonus-with-panics">Bonus with <code>Panics</code></a></li> 93 <li><a href="#miscellaneous-with-contains-len-and-nil">Miscellaneous with <code>Contains</code>, <code>Len</code> and <code>Nil</code></a></li> 94 <li><a href="#write-your-own-comparison">Write your own <code>Comparison</code></a></li> 95 </ul></li> 96 <li><a href="#conclusion">Conclusion…</a></li> 97 </ul> 98 99 <p></div> 100 <!--endtoc--></p> 101 102 <h2 id="assert-and-check"><code>Assert</code> and <code>Check</code></h2> 103 104 <p>Both those functions accept a <code>Comparison</code> (we’ll check what it is later on) and fail the test when that comparison 105 fails. The one difference is that <code>Assert</code> will end the test execution at immediately whereas <code>Check</code> will fail the test 106 and proceed with the rest of the test case. This is similar to <code>FailNow</code> and <code>Fail</code> from the standard library 107 <code>testing</code>. Both have their use cases.</p> 108 109 <p>We’ll Use <code>Assert</code> for the rest of the section but any example here would work with <code>Check</code> too. When we said 110 <code>Comparison</code> above, it’s mainly the <a href="https://godoc.org/gotest.tools/assert#BoolOrComparison">BoolOrComparison</a> interface — it can either be a boolean expression, or a 111 <a href="https://godoc.org/gotest.tools/assert/cmp#Comparison">cmp.Comparison</a> type. <code>Assert</code> and <code>Check</code> code will be <em>smart</em> enough to detect which one it is.</p> 112 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="nx">assert</span><span class="p">.</span><span class="nf">Assert</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">ok</span><span class="p">)</span> 113 <span class="nx">assert</span><span class="p">.</span><span class="nf">Assert</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">)</span> 114 <span class="nx">assert</span><span class="p">.</span><span class="nf">Assert</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">foo</span><span class="p">.</span><span class="nf">IsBar</span><span class="p">())</span></code></pre></div> 115 <p>So far not anything extra-ordinary. Let’s first look at some more <em>helper</em> functions in the <code>assert</code> package and quickly 116 dive a bit deeper with <code>Comparison</code>.</p> 117 118 <h2 id="more-assert-helpers">More <code>assert</code> helpers</h2> 119 120 <p>The additional helper functions are the following</p> 121 122 <ul> 123 <li><code>Equal</code> uses the <code>==</code> operator to assert two values are equal.</li> 124 <li><code>DeepEqual</code> uses <code>google/go-cmp</code> to assert two values are equal (it’s <em>close</em> to <code>reflect.DeepEqual</code> but not 125 quite). We’ll detail a bit more the <em>options</em> part of this function with <code>cmp.DeepEqual</code>.</li> 126 <li><code>Error</code> fails if the error is <code>nil</code> <strong>or</strong> the error message is not the expected one.</li> 127 <li><code>ErrorContains</code> fails if the error is <code>nil</code> <strong>or</strong> the error message does not contain the expected substring.</li> 128 <li><code>ErrorType</code> fails if the error is <code>nil</code> <strong>or</strong> the error type is not the expected type.</li> 129 <li><code>NilError</code> fails if the error is not <code>nil</code>.</li> 130 </ul> 131 132 <p>All those helper functions have a equivalent function in the <code>cmp</code> package that returns a <code>Comparison</code>. I, personally, 133 prefer to use <code>assert.Check</code> or <code>assert.Assert</code> in combination with <code>cmp.Comparison</code> as it allows me to write all my 134 assertions the same way, with built-ins comparison or with my own — i.e. <code>assert.Assert(t, is.Equal(…), "message"</code> or 135 <code>assert.Assert(t, stackIsUp(c, time…), "another message")</code>.</p> 136 137 <h2 id="cmp-dot-comparison"><code>cmp.Comparison</code></h2> 138 139 <p>This is where it get really interesting, <code>gotest.tools</code> tries to make it as easy as possible for you to create 140 appropriate comparison — making you test readable as much as possible.</p> 141 142 <p>Let’s look a bit at the <code>cmp.Comparison</code> type.</p> 143 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Comparison</span> <span class="kd">func</span><span class="p">()</span> <span class="nx">Result</span></code></pre></div> 144 <p>It’s just a function that returns a <code>cmp.Result</code>, so let’s look at <code>cmp.Result</code> definition.</p> 145 <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> 146 <span class="nf">Success</span><span class="p">()</span> <span class="kt">bool</span> 147 <span class="p">}</span></code></pre></div> 148 <p>Result is an <code>interface</code>, thus any <em>struct</em> that provide a function <code>Success</code> that returns a <code>bool</code> can be used as a 149 comparison result, making it really easy to use in your code. There is also existing type of result to make it even 150 quicker to write your own comparison.</p> 151 152 <ul> 153 <li><code>ResultSuccess</code> is a constant which is returned to indicate success.</li> 154 <li><code>ResultFailure</code> and <code>ResultFailureTemplate</code> return a failed Result with a failure message.</li> 155 <li><code>ResultFromError</code> returns <code>ResultSuccess</code> if <code>err</code> is nil. Otherwise <code>ResultFailure</code> is returned with the error 156 message as the failure message. It works a bit like the <code>errors.Wrap</code> function of the <a href="https://github.com/pkg/errors"><code>github.com/pkgs/errors</code></a> 157 package.</li> 158 </ul> 159 160 <p>The <code>cmp</code> package comes with a few defined comparison that, we think, should cover a high number of use-cases. Let’s 161 look at them.</p> 162 163 <h3 id="equality-with-equal-and-deepequal">Equality with <code>Equal</code> and <code>DeepEqual</code></h3> 164 165 <blockquote> 166 <p>Equal uses the == operator to assert two values are equal and fails the test if they are not equal.</p> 167 168 <p>If the comparison fails Equal will use the variable names for x and y as part of the failure message to identify the 169 actual and expected values.</p> 170 171 <p>If either x or y are a multi-line string the failure message will include a unified diff of the two values. If the 172 values only differ by whitespace the unified diff will be augmented by replacing whitespace characters with visible 173 characters to identify the whitespace difference.</p> 174 </blockquote> 175 176 <p>On the other hand…</p> 177 178 <blockquote> 179 <p>DeepEqual uses google/go-cmp (<a href="http://bit.do/go-cmp">http://bit.do/go-cmp</a>) to assert two values are equal and fails the test if they are not 180 equal.</p> 181 182 <p>Package <a href="https://godoc.org/gotest.tools/assert/opt">https://godoc.org/gotest.tools/assert/opt</a> provides some additional commonly used Options.</p> 183 </blockquote> 184 185 <p>Using one or the other is as simple as : if you wrote your <code>if</code> with <code>==</code> then use <code>Equal</code>, otherwise use <code>DeepEqual</code>. 186 <code>DeepEqual</code> (and usually <code>reflect.DeepEqual</code>) is used when you want to compare anything more complex than primitive 187 types. One advantage of using <code>cmp.DeepEqual</code> over <code>reflect.DeepEqual</code> (in an if), is that you get a well crafted 188 message that shows the diff between the expected and the actual structs compared – and you can pass options to it.</p> 189 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="nx">assert</span><span class="p">.</span><span class="nf">Assert</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">DeepEqual</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="s">"a"</span><span class="p">,</span> <span class="s">"b"</span><span class="p">},</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"b"</span><span class="p">,</span> <span class="s">"a"</span><span class="p">}))</span> 190 <span class="c1">// Will print something like 191 </span><span class="c1">// --- result 192 </span><span class="c1">// +++ exp 193 </span><span class="c1">// {[]string}[0]: 194 </span><span class="c1">// -: "a" 195 </span><span class="c1">// +: "b" 196 </span><span class="c1">// {[]string}[1]: 197 </span><span class="c1">// -: "b" 198 </span><span class="c1">// +: "a" 199 </span><span class="c1"></span><span class="nx">foo</span> <span class="o">:=</span> <span class="o">&</span><span class="nf">someType</span><span class="p">(</span><span class="nx">a</span><span class="p">:</span> <span class="s">"with"</span><span class="p">,</span> <span class="nx">b</span><span class="p">:</span> <span class="s">"value"</span><span class="p">)</span> 200 <span class="nx">bar</span> <span class="o">:=</span> <span class="o">&</span><span class="nf">someType</span><span class="p">(</span><span class="nx">a</span><span class="p">:</span> <span class="s">"with"</span><span class="p">,</span> <span class="nx">b</span><span class="p">:</span> <span class="s">"value"</span><span class="p">)</span> 201 <span class="c1">// the following will succeed as foo and bar are _DeepEqual_ 202 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Assert</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">DeepEqual</span><span class="p">(</span><span class="nx">foo</span><span class="p">,</span> <span class="nx">bar</span><span class="p">))</span></code></pre></div> 203 <p>When using <code>DeepEqual</code>, you may end up with really weird behavior(s). You may want to ignore some fields, or consider 204 <code>nil</code> slice or map the same as empty ones ; or more common, your <em>struct</em> contains some unexported fields that you 205 cannot use when comparing (as they are not exported 😓). In those case, you can use <code>go-cmp</code> options.</p> 206 207 <p>Some existing one are :</p> 208 209 <ul> 210 <li><a href="https://godoc.org/github.com/google/go-cmp/cmp/cmpopts#EquateEmpty"><code>EquateEmpty</code></a> returns a Comparer option that determines all maps and slices with a length of zero to be equal, 211 regardless of whether they are nil.</li> 212 <li><a href="https://godoc.org/github.com/google/go-cmp/cmp/cmpopts#IgnoreFields"><code>IgnoreFields</code></a> returns an Option that ignores exported fields of the given names on a single struct type. The struct 213 type is specified by passing in a value of that type.</li> 214 <li><a href="https://godoc.org/github.com/google/go-cmp/cmp/cmpopts#IgnoreUnexported"><code>IgnoreUnexported</code></a> returns an Option that only ignores the immediate unexported fields of a struct, including anonymous 215 fields of unexported types.</li> 216 <li><a href="https://godoc.org/github.com/google/go-cmp/cmp/cmpopts#SortSlices"><code>SortSlices</code></a> returns a Transformer option that sorts all <code>[]V</code></li> 217 <li>… and <a href="https://godoc.org/github.com/google/go-cmp/cmp/cmpopts">more</a> 👼</li> 218 </ul> 219 220 <p><code>gotest.tools</code> also defines some <strong>and</strong> you can define yours ! As an example, <code>gotest.tools</code> defines <code>TimeWithThreshold</code> 221 and <code>DurationWithThreshold</code> that allows to not fails if the time (or duration) is not exactly the same but in the 222 specified threshold we specified. Here is the code for <code>DurationWithThreshold</code> for inspiration.</p> 223 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// DurationWithThreshold returns a gocmp.Comparer for comparing time.Duration. The 224 </span><span class="c1">// Comparer returns true if the difference between the two Duration values is 225 </span><span class="c1">// within the threshold and neither value is zero. 226 </span><span class="c1"></span><span class="kd">func</span> <span class="nf">DurationWithThreshold</span><span class="p">(</span><span class="nx">threshold</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Duration</span><span class="p">)</span> <span class="nx">gocmp</span><span class="p">.</span><span class="nx">Option</span> <span class="p">{</span> 227 <span class="k">return</span> <span class="nx">gocmp</span><span class="p">.</span><span class="nf">Comparer</span><span class="p">(</span><span class="nf">cmpDuration</span><span class="p">(</span><span class="nx">threshold</span><span class="p">))</span> 228 <span class="p">}</span> 229 230 <span class="kd">func</span> <span class="nf">cmpDuration</span><span class="p">(</span><span class="nx">threshold</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Duration</span><span class="p">)</span> <span class="kd">func</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Duration</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> 231 <span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Duration</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> 232 <span class="k">if</span> <span class="nx">x</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">y</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span> 233 <span class="k">return</span> <span class="kc">false</span> 234 <span class="p">}</span> 235 <span class="nx">delta</span> <span class="o">:=</span> <span class="nx">x</span> <span class="o">-</span> <span class="nx">y</span> 236 <span class="k">return</span> <span class="nx">delta</span> <span class="o"><=</span> <span class="nx">threshold</span> <span class="o">&&</span> <span class="nx">delta</span> <span class="o">>=</span> <span class="o">-</span><span class="nx">threshold</span> 237 <span class="p">}</span> 238 <span class="p">}</span></code></pre></div> 239 <p>Another good example for those options is when you want to skip some field. In <a href="https://github.com/docker/docker"><code>docker/docker</code></a> we want to be able to 240 easily check for equality between two service specs, but those might have different <code>CreatedAt</code> and <code>UpdatedAt</code> values 241 that we usually don’t care about – what we want is to make sure it happens in the past 20 seconds. You can easily define 242 an option for that.</p> 243 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">cmpServiceOpts</span><span class="p">()</span> <span class="nx">cmp</span><span class="p">.</span><span class="nx">Option</span> <span class="p">{</span> 244 <span class="kd">const</span> <span class="nx">threshold</span> <span class="p">=</span> <span class="mi">20</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span> 245 246 <span class="c1">// Apply withinThreshold only for the following fields 247 </span><span class="c1"></span> <span class="nx">metaTimeFields</span> <span class="o">:=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">path</span> <span class="nx">cmp</span><span class="p">.</span><span class="nx">Path</span><span class="p">)</span><span class="kt">bool</span> <span class="p">{</span> 248 <span class="k">switch</span> <span class="nx">path</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span> <span class="p">{</span> 249 <span class="k">case</span> <span class="s">"Meta.CreatedAt"</span><span class="p">,</span> <span class="s">"Meta.UpdatedAt"</span><span class="p">:</span> 250 <span class="k">return</span> <span class="kc">true</span> 251 <span class="p">}</span> 252 <span class="k">return</span> <span class="kc">false</span> 253 <span class="p">}</span> 254 <span class="c1">// have a 20s threshold for the time value that will be passed 255 </span><span class="c1"></span> <span class="nx">withinThreshold</span> <span class="o">:=</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Comparer</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> 256 <span class="nx">delta</span> <span class="o">:=</span> <span class="nx">x</span><span class="p">.</span><span class="nf">Sub</span><span class="p">(</span><span class="nx">y</span><span class="p">)</span> 257 <span class="k">return</span> <span class="nx">delta</span> <span class="p"><</span> <span class="nx">threshold</span> <span class="o">&&</span> <span class="nx">delta</span> <span class="p">></span> <span class="o">-</span><span class="nx">threshold</span> 258 <span class="p">})</span> 259 260 <span class="k">return</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">FilterPath</span><span class="p">(</span><span class="nx">metaTimeFields</span><span class="p">,</span> <span class="nx">withinThreshold</span><span class="p">)</span> 261 <span class="p">}</span></code></pre></div> 262 <p>I recommend you look at the <a href="https://godoc.org/gotest.tools/assert/opt">gotest.tools/assert/opt</a> documentation to see which one are defined and how to use them.</p> 263 264 <h3 id="errors-with-error-errorcontains-and-errortype">Errors with <code>Error</code>, <code>ErrorContains</code> and <code>ErrorType</code></h3> 265 266 <p>Checking for errors is <strong>very common</strong> in Go, having <code>Comparison</code> function for it was a requirement.</p> 267 268 <ul> 269 <li><code>Error</code> fails if the error is <code>nil</code> <strong>or</strong> the error message is not the expected one.</li> 270 <li><code>ErrorContains</code> fails if the error is <code>nil</code> <strong>or</strong> the error message does not contain the expected substring.</li> 271 <li><code>ErrorType</code> fails if the error is <code>nil</code> <strong>or</strong> the error type is not the expected type.</li> 272 </ul> 273 274 <p>Let’s first look at the most used : <code>Error</code> and <code>ErrorContains</code>.</p> 275 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">err</span> <span class="kt">error</span> 276 <span class="c1">// will fail with : expected an error, got nil 277 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="s">"message in a bottle"</span><span class="p">))</span> 278 <span class="nx">err</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">Wrap</span><span class="p">(</span><span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"other"</span><span class="p">),</span> <span class="s">"wrapped"</span><span class="p">)</span> 279 <span class="c1">// will fail with : expected error "other", got "wrapped: other" 280 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="s">"other"</span><span class="p">))</span> 281 <span class="c1">// will succeed 282 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">ErrorContains</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="s">"other"</span><span class="p">))</span></code></pre></div> 283 <p>As you can see <code>ErrorContains</code> is especially useful when working with <em>wrapped</em> errors. 284 Now let’s look at <code>ErrorType</code>.</p> 285 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">err</span> <span class="kt">error</span> 286 <span class="c1">// will fail with : error is nil, not StubError 287 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">ErrorType</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">StubError</span><span class="p">{}))</span> 288 289 <span class="nx">err</span> <span class="o">:=</span> <span class="nx">StubError</span><span class="p">{</span><span class="s">"foo"</span><span class="p">}</span> 290 <span class="c1">// will succeed 291 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">ErrorType</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">StubError</span><span class="p">{}))</span> 292 293 <span class="c1">// Note that it also work with a function returning an error 294 </span><span class="c1"></span><span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{}</span> 295 <span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">ErrorType</span><span class="p">(</span><span class="nx">foo</span><span class="p">,</span> <span class="nx">StubError</span><span class="p">{}))</span></code></pre></div> 296 <h3 id="bonus-with-panics">Bonus with <code>Panics</code></h3> 297 298 <p>Sometimes, a code is supposed to <em>panic</em>, see <a href="https://golang.org/doc/effective%5Fgo.html#panic">Effective Go (#Panic)</a> for more information. And thus, you may want to make 299 sure you’re code panics in such cases. It’s always a bit tricky to test a code that panic as you have to use a deferred 300 function to recover the panic — but then if the panic doesn’t happen how do you fail the test ?</p> 301 302 <p>This is where <code>Panics</code> comes handy.</p> 303 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nx">shouldPanic</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span> 304 <span class="k">if</span> <span class="nx">shouldPanic</span> <span class="p">{</span> 305 <span class="nb">panic</span><span class="p">(</span><span class="s">"booooooooooh"</span><span class="p">)</span> 306 <span class="p">}</span> 307 <span class="c1">// don't worry, be happy 308 </span><span class="c1"></span><span class="p">}</span> 309 <span class="c1">// will fail with : did not panic 310 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Panics</span><span class="p">(</span><span class="nf">foo</span><span class="p">(</span><span class="kc">false</span><span class="p">)))</span> 311 <span class="c1">// will succeed 312 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Panics</span><span class="p">(</span><span class="nf">foo</span><span class="p">(</span><span class="kc">true</span><span class="p">)))</span></code></pre></div> 313 <h3 id="miscellaneous-with-contains-len-and-nil">Miscellaneous with <code>Contains</code>, <code>Len</code> and <code>Nil</code></h3> 314 315 <p>Those last three <em>built-in</em> <code>Comparison</code> are pretty straightforward.</p> 316 317 <ul> 318 <li><p><code>Contains</code> succeeds if item is in collection. Collection may be a string, map, slice, or array.</p> 319 320 <p>If collection is a string, item must also be a string, and is compared using <code>strings.Contains()</code>. If collection is a 321 Map, contains will succeed if item is a key in the map. If collection is a slice or array, item is compared to each 322 item in the sequence using <code>=reflect.DeepEqual()=</code>.</p></li> 323 324 <li><p><code>Len</code> succeeds if the sequence has the expected length.</p></li> 325 326 <li><p><code>Nil</code> succeeds if obj is a nil interface, pointer, or function.</p></li> 327 </ul> 328 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// Contains works on string, map, slice or arrays 329 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"foobar"</span><span class="p">,</span> <span class="s">"foo"</span><span class="p">))</span> 330 <span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Contains</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="s">"a"</span><span class="p">,</span> <span class="s">"b"</span><span class="p">,</span> <span class="s">"c"</span><span class="p">},</span> <span class="s">"b"</span><span class="p">))</span> 331 <span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span><span class="p">{</span><span class="s">"a"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"b"</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="s">"c"</span><span class="p">:</span> <span class="mi">4</span><span class="p">},</span> <span class="s">"b"</span><span class="p">))</span> 332 333 <span class="c1">// Len also works on string, map, slice or arrays 334 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Len</span><span class="p">(</span><span class="s">"foobar"</span><span class="p">,</span> <span class="mi">6</span><span class="p">))</span> 335 <span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Len</span><span class="p">([]</span><span class="kt">string</span><span class="p">{</span><span class="s">"a"</span><span class="p">,</span> <span class="s">"b"</span><span class="p">,</span> <span class="s">"c"</span><span class="p">},</span> <span class="mi">3</span><span class="p">))</span> 336 <span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Len</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span><span class="p">{</span><span class="s">"a"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"b"</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="s">"c"</span><span class="p">:</span> <span class="mi">4</span><span class="p">},</span> <span class="mi">3</span><span class="p">))</span> 337 338 <span class="c1">// Nil 339 </span><span class="c1"></span><span class="kd">var</span> <span class="nx">foo</span> <span class="o">*</span><span class="nx">MyStruc</span> 340 <span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Nil</span><span class="p">(</span><span class="nx">foo</span><span class="p">))</span> 341 <span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">Nil</span><span class="p">(</span><span class="nf">bar</span><span class="p">()))</span></code></pre></div> 342 <p>But let’s not waste more time and let’s see how to write our own <code>Comparison</code> !</p> 343 344 <h3 id="write-your-own-comparison">Write your own <code>Comparison</code></h3> 345 346 <p>One of the main aspect of <code>gotest.tools/assert</code> is to make it easy for developer to write as less boilerplate code as 347 possible while writing tests. Writing your own <code>Comparison</code> allows you to write a well named function that will be easy 348 to read and that can be re-used across your tests.</p> 349 350 <p>Let’s look back at the <code>cmp.Comparison</code> and <code>cmp.Result</code> types.</p> 351 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Comparison</span> <span class="kd">func</span><span class="p">()</span> <span class="nx">Result</span> 352 353 <span class="kd">type</span> <span class="nx">Result</span> <span class="kd">interface</span> <span class="p">{</span> 354 <span class="nf">Success</span><span class="p">()</span> <span class="kt">bool</span> 355 <span class="p">}</span></code></pre></div> 356 <p>A <code>Comparison</code> for <code>assert.Check</code> or <code>assert.Check</code> is a function that return a <code>Result</code>, it’s pretty straightforward to 357 implement, especially with <code>cmp.ResultSuccess</code> and <code>cmp.ResultFailure(…)</code> (as seen previously).</p> 358 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">regexPattern</span><span class="p">(</span><span class="nx">value</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">pattern</span> <span class="kt">string</span><span class="p">)</span> <span class="nx">cmp</span><span class="p">.</span><span class="nx">Comparison</span> <span class="p">{</span> 359 <span class="k">return</span> <span class="kd">func</span><span class="p">()</span> <span class="nx">cmp</span><span class="p">.</span><span class="nx">Result</span> <span class="p">{</span> 360 <span class="nx">re</span> <span class="o">:=</span> <span class="nx">regexp</span><span class="p">.</span><span class="nf">MustCompile</span><span class="p">(</span><span class="nx">pattern</span><span class="p">)</span> 361 <span class="k">if</span> <span class="nx">re</span><span class="p">.</span><span class="nf">MatchString</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span> 362 <span class="k">return</span> <span class="nx">cmp</span><span class="p">.</span><span class="nx">ResultSuccess</span> 363 <span class="p">}</span> 364 <span class="k">return</span> <span class="nx">cmp</span><span class="p">.</span><span class="nf">ResultFailure</span><span class="p">(</span> 365 <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">"%q did not match pattern %q"</span><span class="p">,</span> <span class="nx">value</span><span class="p">,</span> <span class="nx">pattern</span><span class="p">))</span> 366 <span class="p">}</span> 367 <span class="p">}</span> 368 369 <span class="c1">// To use it 370 </span><span class="c1"></span><span class="nx">assert</span><span class="p">.</span><span class="nf">Check</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nf">regexPattern</span><span class="p">(</span><span class="s">"12345.34"</span><span class="p">,</span> <span class="s">`\d+.\d\d`</span><span class="p">))</span></code></pre></div> 371 <p>As you can see, it’s pretty easy to implement, and you can do quite a lot in there easily. If a function call returns an 372 error inside of your <code>Comparison</code> function, you can use <code>cmp.ResultFromError</code> for example. Having something like 373 <code>assert.Check(t, isMyServerUp(":8080"))</code> is way more readable than a 30-line of code to check it.</p> 374 375 <h2 id="conclusion">Conclusion…</h2> 376 377 <p>… and that’s a wrap. We only looked at the <code>assert</code> package of <a href="https://gotest.tools"><code>gotest.tools</code></a> so far, but it’s already quite a bit to process.</p> 378 379 <p>We’ve seen :</p> 380 381 <ul> 382 <li>the main functions provided by this package : <code>assert.Assert</code> and <code>assert.Check</code></li> 383 <li>some helper functions like <code>assert.NilError</code>, …</li> 384 <li>the <code>assert/cmp</code>, and <code>assert/opt</code> sub-package that allows you to write more custom <code>Comparison</code></li> 385 </ul> 386 387 <p>Next time, we’ll look at the <code>skip</code> package, that is a really simple wrapper on top of <code>testing.Skip</code> function.</p> 388 389 390 </article> 391 <hr /> 392 <div class="prev-next"> 393 394 <a class="paging-link prev" href="/posts/2018-09-01-gotest-tools-skip/" title="Golang testing — gotest.tools skip">← Previous post</a> 395 396 397 398 <a class="paging-link next" href="/posts/2018-07-28-gotest-tools-intro/" title="Golang testing — gotest.tools introduction">Next post →</a> 399 400 </div> 401 402 </div> 403 </div> 404 405 <footer> 406 <nav> 407 408 <a href="/">home</a> 409 <span class="text-muted"> | </span> 410 411 <a href="/about">about</a> 412 <span class="text-muted"> | </span> 413 414 <a href="/archive">archive</a> 415 <span class="text-muted"> | </span> 416 417 <a href="/categories">categories</a> 418 <span class="text-muted"> | </span> 419 420 <a href="/tags">tags</a> 421 <span class="text-muted"> | </span> 422 423 <a href="https://twitter.com/vdemeest">twitter</a> 424 <span class="text-muted"> | </span> 425 426 <a href="https://github.com/vdemeester">github</a> 427 <span class="text-muted"> | </span> 428 429 <a href="https://vincent.demeester.fr/index.xml">rss</a> 430 </nav> 431 <br/> 432 <address> 433 <span class="copyright"> 434 Content and design by Vincent Demeester 435 (<a rel="licence" href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Some rights reserved</a>) 436 </span><br /> 437 <span class="engine"> 438 Powered by <a href="https://gohugo.io/">Hugo</a> and <a href="https://github.com/kaushalmodi/ox-hugo/">ox-hugo</a> 439 </span> 440 </address> 441 </footer> 442 </body> 443