home

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

index.html (35532B)


      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/2017-01-01-go-testing-functionnal-builders/">
     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="en"/>
     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 — functional arguments for wonderful builders</h1><a href='https://vincent.demeester.fr/posts/2017-01-01-go-testing-functionnal-builders/'></a>
     37         <address class="signature">
     38           <span class="date">Sun, 1 January, 2017</span>
     39           <span class="words">(1600 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-golang"><a href="/tags/#golang">golang<span>12</span></a></li>
     50 	  
     51 	  
     52 	  <li class="tag tag-testing"><a href="/tags/#testing">testing<span>11</span></a></li>
     53 	  
     54 	  
     55 	  <li class="tag tag-builder"><a href="/tags/#builder">builder<span>1</span></a></li>
     56 	  
     57 	  
     58 	  <li class="tag tag-functionnal"><a href="/tags/#functionnal">functionnal<span>2</span></a></li>
     59 	  
     60 	  
     61 	  <li class="tag tag-java"><a href="/tags/#java">java<span>4</span></a></li>
     62 	  
     63 	  
     64 	  <li class="tag tag-featured"><a href="/tags/#featured">featured<span>2</span></a></li>
     65 	  
     66 	  <br/>
     67 	  
     68 	</ul>
     69       </header>
     70       
     71       
     72       
     73       <blockquote>
     74 <p>
     75 Programming is not easy; even the best programmers are incapable of
     76 writing programs that work exactly as intended every time. Therefore
     77 an important part of the software development process is
     78 testing. Writing tests for our code is a good way to ensure quality
     79 and improve reliability.
     80 </p>
     81 </blockquote>
     82 
     83 <p>
     84 Go programs, when properly implemented, are fairly simple to test
     85 programatically. The <code>testing</code> built-in library and the features of
     86 the language itself offer plenty of ways to write good tests. As this
     87 is a subject I particularly like, I'm gonna write a bunch of articles
     88 about it, that, <i>hopefully</i> do not get old or boring.
     89 </p>
     90 
     91 <p>
     92 I'm not going to start by introducing how <code>testing</code> works, it's
     93 already widely described in <a href="https://golang.org/pkg/testing/">the <code>testing</code> godoc</a>, <a href="https://blog.golang.org/examples">some</a> <a href="https://www.golang-book.com/books/intro/12">articles</a> and
     94 <a href="https://jonathanmh.com/golang-unit-testing-for-absolute-beginners/">blogs</a>. I'm going to jump ahead on a more advanced techinque to write
     95 tests, the <code>builders</code> for tests.
     96 </p>
     97 
     98 <p>
     99 One of the most important characteristic of a <b>unit test</b> (and any
    100 type of test really) is <b>readability</b>. This means it should be <i>easy
    101 to read</i> but most importantly it should <b>clearly show the intent of
    102 the test</b>. The setup (and cleanup) of the tests should be as small as
    103 possible to avoid the noise. And as we are going to see below, <code>go</code>
    104 makes it pretty easy to do so.
    105 </p>
    106 
    107 <div id="outline-container-org1afcfe7" class="outline-2">
    108 <h2 id="org1afcfe7">Builders in tests</h2>
    109 <div class="outline-text-2" id="text-org1afcfe7">
    110 <p>
    111 Sometimes, your need to create data structure for your test that
    112 might take a lot of line and introduce noise. In <code>golang</code> we don't
    113 have method overload or even <i>constructors</i> as some other language
    114 have. This means most of the time, we end up building our data using
    115 directly the struct expression, as below.
    116 </p>
    117 
    118 <div class="org-src-container">
    119 <pre class="src src-go"><span class="org-rainbow-identifiers-identifier-5">node</span> := &amp;<span class="org-type">Node</span>{
    120         <span class="org-constant">Name</span>: <span class="org-string">"carthage"</span>,
    121         <span class="org-constant">Hostname</span>: <span class="org-string">"carthage.sbr.pm"</span>,
    122         <span class="org-constant">Platform</span>: <span class="org-type">Platform</span>{
    123                 <span class="org-constant">Architecture</span>: <span class="org-string">"x86_64"</span>,
    124                 <span class="org-constant">OS</span>:           <span class="org-string">"linux"</span>,
    125         },
    126 }
    127 </pre>
    128 </div>
    129 
    130 <p>
    131 Let's imagine we have a <code>Validate</code> function that make sure the
    132 specified <code>Node</code> is supported on our structure. We would write some
    133 tests that ensure that.
    134 </p>
    135 
    136 <div class="org-src-container">
    137 <pre class="src src-go"><span class="org-keyword">func</span> <span class="org-function-name">TestValidateLinuxIsSupported</span>(<span class="org-rainbow-identifiers-identifier-8">t</span> *<span class="org-type">testing.T</span>) {
    138         <span class="org-rainbow-identifiers-identifier-6">valid</span> := <span class="org-function-name">Validate</span>(&amp;<span class="org-type">Node</span>{
    139                 <span class="org-constant">Name</span>: <span class="org-string">"carthage"</span>,
    140                 <span class="org-constant">Hostname</span>: <span class="org-string">"carthage.sbr.pm"</span>,
    141                 <span class="org-constant">Platform</span>: &amp;<span class="org-type">Platform</span>{
    142                         <span class="org-constant">Architecture</span>: <span class="org-string">"x86_64"</span>,
    143                         <span class="org-constant">OS</span>:           <span class="org-string">"linux"</span>,
    144                 },
    145         })
    146         <span class="org-keyword">if</span> !<span class="org-rainbow-identifiers-identifier-6">valid</span> {
    147                 <span class="org-rainbow-identifiers-identifier-8">t</span>.<span class="org-function-name">Fatal</span>(<span class="org-string">"linux should be supported, it was not"</span>)
    148         }
    149 }
    150 
    151 <span class="org-keyword">func</span> <span class="org-function-name">TestValidateDarwinIsNotSupported</span>(<span class="org-rainbow-identifiers-identifier-8">t</span> *<span class="org-type">testing.T</span>) {
    152         <span class="org-rainbow-identifiers-identifier-6">valid</span> := <span class="org-function-name">Validate</span>(&amp;<span class="org-type">Node</span>{
    153                 <span class="org-constant">Name</span>: <span class="org-string">"babylon"</span>,
    154                 <span class="org-constant">Hostname</span>: <span class="org-string">"babylon.sbr.pm"</span>,
    155                 <span class="org-constant">Platform</span>: &amp;<span class="org-type">Platform</span>{
    156                         <span class="org-constant">Architecture</span>: <span class="org-string">"x86_64"</span>,
    157                         <span class="org-constant">OS</span>:           <span class="org-string">"darwin"</span>,
    158                 },
    159         })
    160         <span class="org-keyword">if</span> <span class="org-rainbow-identifiers-identifier-6">valid</span> {
    161                 <span class="org-rainbow-identifiers-identifier-8">t</span>.<span class="org-function-name">Fatal</span>(<span class="org-string">"darwin should not be supported, it was"</span>)
    162         }
    163 }
    164 </pre>
    165 </div>
    166 
    167 <p>
    168 This is quickly hard to read, there is too much noise on that
    169 test. We setup a whole <code>Node</code> struct, but the only thing we really
    170 intend to test is the <code>Platform.OS</code> part. The rest is just required
    171 fields for the function to correctly compile and run.
    172 </p>
    173 
    174 <p>
    175 This is where test builders (and builders in general) comes into
    176 play. In <a href="http://www.growing-object-oriented-software.com/">Growing Object-Oriented Software Guided by Tests</a>, the
    177 Chapter 22 "Constructing Complex Test Data" is exactly about that
    178 and guide us through the why and the how of these builders. The
    179 examples in the book are in <code>java</code> and uses wisely the
    180 object-oriented nature of the language. Here is an example from the
    181 book.
    182 </p>
    183 
    184 <div class="org-src-container">
    185 <pre class="src src-java"><span class="org-comment-delimiter">// </span><span class="org-comment">I just want an order from a customer that has no post code</span>
    186 <span class="org-type">Order</span> <span class="org-variable-name">order</span> = <span class="org-rainbow-identifiers-identifier-12">anOrder</span>()
    187     .<span class="org-rainbow-identifiers-identifier-7">from</span>(<span class="org-rainbow-identifiers-identifier-13">aCustomer</span>().<span class="org-rainbow-identifiers-identifier-11">with</span>(<span class="org-rainbow-identifiers-identifier-13">anAddress</span>().<span class="org-rainbow-identifiers-identifier-3">withNotPostCode</span>()))
    188     .<span class="org-rainbow-identifiers-identifier-10">build</span>()
    189 </pre>
    190 </div>
    191 
    192 <p>
    193 These builders helps <b>keep tests expressive</b>, as it's pretty obvious
    194 when reading it, what we want to test. They remove the <b>visual
    195 noise</b> you have when building an object (or a <code>struct{}</code> in Go) and
    196 allows you to put sane default. They also make <b>tests resilient to
    197 change</b>. If the structure changes, only the builder has to be
    198 updated, not the tests depending on it. They also make default case
    199 really simple to write, and special cases not much more complicated.
    200 </p>
    201 </div>
    202 </div>
    203 
    204 <div id="outline-container-orgff90129" class="outline-2">
    205 <h2 id="orgff90129">Builder in Go</h2>
    206 <div class="outline-text-2" id="text-orgff90129">
    207 <p>
    208 The naive way to create builders in <code>go</code> could be to create a
    209 <code>builder</code> struct that have methods to construct the final struct and
    210 a <code>build</code> method. Let's see how it looks.
    211 </p>
    212 
    213 <div class="org-src-container">
    214 <pre class="src src-go"><span class="org-keyword">func</span> <span class="org-function-name">ANode</span>() *<span class="org-type">NodeBuilder</span> {
    215         <span class="org-keyword">return</span> &amp;<span class="org-type">NodeBuilder</span>{
    216                 <span class="org-constant">node</span>: &amp;<span class="org-type">Node</span>{
    217                         <span class="org-constant">Name</span>: <span class="org-string">"node"</span>,
    218                         <span class="org-comment-delimiter">// </span><span class="org-comment">Other defaults</span>
    219                 },
    220         }
    221 }
    222 
    223 <span class="org-keyword">type</span> <span class="org-type">NodeBuilder</span> <span class="org-keyword">struct</span> {
    224         <span class="org-rainbow-identifiers-identifier-5">node</span> *<span class="org-rainbow-identifiers-identifier-12">Node</span>
    225 }
    226 
    227 <span class="org-keyword">func</span> (<span class="org-rainbow-identifiers-identifier-1">b</span> *<span class="org-type">NodeBuilder</span>) <span class="org-function-name">Build</span>() *<span class="org-type">Node</span> {
    228         <span class="org-keyword">return</span> <span class="org-rainbow-identifiers-identifier-1">b</span>.<span class="org-rainbow-identifiers-identifier-5">node</span>
    229 }
    230 
    231 <span class="org-keyword">func</span> (<span class="org-rainbow-identifiers-identifier-1">b</span> *<span class="org-type">NodeBuilder</span>) <span class="org-function-name">Hostname</span>(<span class="org-rainbow-identifiers-identifier-3">hostname</span> <span class="org-type">string</span>) *<span class="org-type">NodeBuilder</span> {
    232         <span class="org-rainbow-identifiers-identifier-1">b</span>.<span class="org-rainbow-identifiers-identifier-5">node</span>.<span class="org-rainbow-identifiers-identifier-10">Hostname</span> = <span class="org-rainbow-identifiers-identifier-3">hostname</span>
    233         <span class="org-keyword">return</span> <span class="org-rainbow-identifiers-identifier-1">b</span>
    234 }
    235 
    236 <span class="org-keyword">func</span> (<span class="org-rainbow-identifiers-identifier-1">b</span> *<span class="org-type">NodeBuilder</span>) <span class="org-function-name">Name</span>(<span class="org-rainbow-identifiers-identifier-3">name</span> <span class="org-type">string</span>) *<span class="org-type">NodeBuilder</span> {
    237         <span class="org-rainbow-identifiers-identifier-1">b</span>.<span class="org-rainbow-identifiers-identifier-5">node</span>.<span class="org-rainbow-identifiers-identifier-8">Name</span> = <span class="org-rainbow-identifiers-identifier-3">name</span>
    238         <span class="org-keyword">return</span> <span class="org-rainbow-identifiers-identifier-1">b</span>
    239 }
    240 
    241 <span class="org-keyword">func</span> (<span class="org-rainbow-identifiers-identifier-1">b</span> *<span class="org-type">NodeBuilder</span>) <span class="org-function-name">Platform</span>(<span class="org-rainbow-identifiers-identifier-14">platform</span> *<span class="org-type">Platform</span>) *<span class="org-type">NodeBuilder</span> {
    242         <span class="org-rainbow-identifiers-identifier-1">b</span>.<span class="org-rainbow-identifiers-identifier-5">node</span>.<span class="org-rainbow-identifiers-identifier-4">Platform</span> = <span class="org-rainbow-identifiers-identifier-14">platform</span>
    243         <span class="org-keyword">return</span> <span class="org-rainbow-identifiers-identifier-1">b</span>
    244 }
    245 </pre>
    246 </div>
    247 
    248 <p>
    249 This looks decent, and using it is pretty straightforward. At least
    250 it make building the <code>struct</code> more expressive, less noisy and
    251 resilient to change. We can update the previous test as follow.
    252 </p>
    253 
    254 <div class="org-src-container">
    255 <pre class="src src-go"><span class="org-keyword">func</span> <span class="org-function-name">TestValidateLinuxIsSupported</span>(<span class="org-rainbow-identifiers-identifier-8">t</span> *<span class="org-type">testing.T</span>) {
    256         <span class="org-rainbow-identifiers-identifier-6">valid</span> := <span class="org-function-name">Validate</span>(<span class="org-function-name">ANode</span>().<span class="org-function-name">Platform</span>(&amp;<span class="org-type">Platform</span>{
    257                 <span class="org-constant">Architecture</span>: <span class="org-string">"x86_64"</span>,
    258                 <span class="org-constant">OS</span>:           <span class="org-string">"linux"</span>,
    259         }).<span class="org-function-name">Build</span>())
    260         <span class="org-keyword">if</span> !<span class="org-rainbow-identifiers-identifier-6">valid</span> {
    261                 <span class="org-rainbow-identifiers-identifier-8">t</span>.<span class="org-function-name">Fatal</span>(<span class="org-string">"linux should be supported, it was not"</span>)
    262         }
    263 }
    264 
    265 <span class="org-keyword">func</span> <span class="org-function-name">TestValidateDarwinIsNotSupported</span>(<span class="org-rainbow-identifiers-identifier-8">t</span> *<span class="org-type">testing.T</span>) {
    266         <span class="org-rainbow-identifiers-identifier-6">valid</span> := <span class="org-function-name">Validate</span>(<span class="org-function-name">ANode</span>().<span class="org-function-name">Platform</span>(&amp;<span class="org-type">Platform</span>{
    267                 <span class="org-constant">Architecture</span>: <span class="org-string">"x86_64"</span>,
    268                 <span class="org-constant">OS</span>:           <span class="org-string">"darwin"</span>,
    269         }).<span class="org-function-name">Build</span>())
    270         <span class="org-keyword">if</span> <span class="org-rainbow-identifiers-identifier-6">valid</span> {
    271                 <span class="org-rainbow-identifiers-identifier-8">t</span>.<span class="org-function-name">Fatal</span>(<span class="org-string">"darwin should not be supported, it was"</span>)
    272         }
    273 }
    274 </pre>
    275 </div>
    276 
    277 <p>
    278 There is room for improvement :
    279 </p>
    280 
    281 <ul class="org-ul">
    282 <li>There is still some noise, mainly <code>build()</code> and the platform
    283 <code>struct</code>, as it still shows too much.</li>
    284 <li>It's not that extensible yet. If you want to update the <code>Node</code> a
    285 certain way that the builder is not written for, you have to
    286 update the builder.</li>
    287 <li>The <code>NodeBuilder</code> struct feels a little empty, it's just there to
    288 hold on the <code>Node</code> being constructed until it is <code>build</code>.</li>
    289 </ul>
    290 
    291 <p>
    292 One improvement we could make is to have a <code>Platform</code> builder, even
    293 if it's a small struct here. Let's do that in the same way we did
    294 with <code>Node</code>.
    295 </p>
    296 
    297 <div class="org-src-container">
    298 <pre class="src src-go"><span class="org-keyword">func</span> <span class="org-function-name">APlatform</span>() *<span class="org-type">PlatformBuilder</span> {
    299         <span class="org-keyword">return</span> &amp;<span class="org-type">PlatformBuilder</span>{
    300                 <span class="org-constant">platform</span>: &amp;<span class="org-type">Platform</span>{
    301                         <span class="org-constant">Architecture</span>: <span class="org-string">"x64_86"</span>,
    302                         <span class="org-constant">OS</span>: <span class="org-string">"linux"</span>,
    303                 },
    304         }
    305 }
    306 
    307 <span class="org-keyword">type</span> <span class="org-type">PlatformBuilder</span> <span class="org-keyword">struct</span>{
    308         <span class="org-rainbow-identifiers-identifier-14">platform</span> *<span class="org-rainbow-identifiers-identifier-4">Platform</span>
    309 }
    310 
    311 <span class="org-keyword">func</span> (<span class="org-rainbow-identifiers-identifier-1">b</span> *<span class="org-type">PlatformBuilder</span>) <span class="org-function-name">Build</span>() *<span class="org-type">Platform</span> {
    312         <span class="org-keyword">return</span> <span class="org-rainbow-identifiers-identifier-1">b</span>.<span class="org-rainbow-identifiers-identifier-14">platform</span>
    313 }
    314 
    315 <span class="org-keyword">func</span> (<span class="org-rainbow-identifiers-identifier-1">b</span> *<span class="org-type">PlatformBuilder</span>) <span class="org-function-name">OS</span>(<span class="org-rainbow-identifiers-identifier-2">os</span> <span class="org-type">string</span>) *<span class="org-type">PlatformBuilder</span> {
    316         <span class="org-rainbow-identifiers-identifier-1">b</span>.<span class="org-rainbow-identifiers-identifier-14">platform</span>.<span class="org-rainbow-identifiers-identifier-12">OS</span> = <span class="org-rainbow-identifiers-identifier-2">os</span>
    317         <span class="org-keyword">return</span> <span class="org-rainbow-identifiers-identifier-1">b</span>
    318 }
    319 </pre>
    320 </div>
    321 
    322 <p>
    323 And our tests becomes 🐻.
    324 </p>
    325 
    326 <div class="org-src-container">
    327 <pre class="src src-go"><span class="org-keyword">func</span> <span class="org-function-name">TestValidateLinuxIsSupported</span>(<span class="org-rainbow-identifiers-identifier-8">t</span> *<span class="org-type">testing.T</span>) {
    328         <span class="org-rainbow-identifiers-identifier-6">valid</span> := <span class="org-function-name">Validate</span>(<span class="org-function-name">ANode</span>().<span class="org-function-name">Platform</span>(
    329                 <span class="org-function-name">APlatform</span>().<span class="org-function-name">OS</span>(<span class="org-string">"linux"</span>).<span class="org-function-name">Build</span>()
    330         ).<span class="org-function-name">Build</span>())
    331         <span class="org-keyword">if</span> !<span class="org-rainbow-identifiers-identifier-6">valid</span> {
    332                 <span class="org-rainbow-identifiers-identifier-8">t</span>.<span class="org-function-name">Fatal</span>(<span class="org-string">"linux should be supported, it was not"</span>)
    333         }
    334 }
    335 
    336 <span class="org-keyword">func</span> <span class="org-function-name">TestValidateDarwinIsNotSupported</span>(<span class="org-rainbow-identifiers-identifier-8">t</span> *<span class="org-type">testing.T</span>) {
    337         <span class="org-rainbow-identifiers-identifier-6">valid</span> := <span class="org-function-name">Validate</span>(<span class="org-function-name">ANode</span>().<span class="org-function-name">Platform</span>(
    338                 <span class="org-function-name">APlatform</span>().<span class="org-function-name">OS</span>(<span class="org-string">"darwin"</span>).<span class="org-function-name">Build</span>()
    339         ).<span class="org-function-name">Build</span>())
    340         <span class="org-keyword">if</span> <span class="org-rainbow-identifiers-identifier-6">valid</span> {
    341                 <span class="org-rainbow-identifiers-identifier-8">t</span>.<span class="org-function-name">Fatal</span>(<span class="org-string">"darwin should not be supported, it was"</span>)
    342         }
    343 }
    344 </pre>
    345 </div>
    346 
    347 <p>
    348 It does not really improve the visual noise as there is now quite a
    349 few duplication : several <code>build</code>, <code>APlatform</code> inside <code>Platform</code>, …
    350 It is a small improvement on readability but not that much compared
    351 to the previous one. This is were the Go language features comes
    352 into play.
    353 </p>
    354 </div>
    355 </div>
    356 
    357 <div id="outline-container-org28e3042" class="outline-2">
    358 <h2 id="org28e3042">Functional arguments to the rescue</h2>
    359 <div class="outline-text-2" id="text-org28e3042">
    360 <p>
    361 Go has two interesting feature that are going to be useful here.
    362 </p>
    363 
    364 <p>
    365 First, a function in Go is a type on its own and thus is considered
    366 a <i>first class citizen</i>. It means it's possible to pass a function
    367 as argument, or define a variable that holds it.
    368 </p>
    369 
    370 <div class="org-src-container">
    371 <pre class="src src-go"><span class="org-keyword">func</span> <span class="org-function-name">ApplyTo</span>(<span class="org-rainbow-identifiers-identifier-8">s</span> <span class="org-type">string</span>, <span class="org-rainbow-identifiers-identifier-12">fn</span> <span class="org-keyword">func</span>(<span class="org-type">string</span>) <span class="org-type">string</span>) <span class="org-type">string</span> {
    372         <span class="org-keyword">return</span> <span class="org-function-name">fn</span>(<span class="org-rainbow-identifiers-identifier-8">s</span>)
    373 }
    374 
    375 <span class="org-keyword">func</span> <span class="org-function-name">world</span>(<span class="org-rainbow-identifiers-identifier-8">s</span> <span class="org-type">string</span>) <span class="org-type">string</span> {
    376         <span class="org-keyword">return</span> <span class="org-rainbow-identifiers-identifier-9">fmt</span>.<span class="org-function-name">Sprintf</span>(<span class="org-string">"%s, world!"</span>, <span class="org-rainbow-identifiers-identifier-8">s</span>)
    377 }
    378 
    379 <span class="org-comment-delimiter">// </span><span class="org-comment">Usage</span>
    380 <span class="org-rainbow-identifiers-identifier-1">a</span> := <span class="org-function-name">ApplyTo</span>(<span class="org-string">"hello"</span>, <span class="org-rainbow-identifiers-identifier-9">world</span>)
    381 <span class="org-comment-delimiter">// </span><span class="org-comment">a == "hello, world!"</span>
    382 </pre>
    383 </div>
    384 
    385 <p>
    386 The second feature that comes into play here, is the possiblity to
    387 have <i>variadic</i> functions. A variadic function is a function that
    388 takes a variable number of arguments (from <code>0</code> to any number of
    389 argument).
    390 </p>
    391 
    392 <div class="org-src-container">
    393 <pre class="src src-go"><span class="org-keyword">func</span> <span class="org-function-name">Print</span>(<span class="org-rainbow-identifiers-identifier-9">strs</span> ...<span class="org-type">string</span>) <span class="org-type">string</span> {
    394         <span class="org-keyword">for</span> <span class="org-rainbow-identifiers-identifier-8">_</span>, <span class="org-rainbow-identifiers-identifier-8">s</span> := <span class="org-keyword">range</span> <span class="org-rainbow-identifiers-identifier-9">strs</span> {
    395                 <span class="org-rainbow-identifiers-identifier-9">fmt</span>.<span class="org-function-name">Println</span>(<span class="org-rainbow-identifiers-identifier-8">s</span>)
    396         }
    397 }
    398 </pre>
    399 </div>
    400 
    401 <p>
    402 As we are going to see below, combining these two feature makes our
    403 builders pretty easy to write and to use with simple case, while
    404 staying very customizable, even outside of the builder. This is
    405 really well described in a talk from Dave Cheney : <a href="https://www.youtube.com/watch?v=24lFtGHWxAQ&amp;index=15&amp;list=PLMW8Xq7bXrG58Qk-9QSy2HRh2WVeIrs7e">Functional
    406 options for friendly APIs</a> (<a href="https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis">transcription</a>).
    407 </p>
    408 
    409 <p>
    410 Let's apply that to our new builders.
    411 </p>
    412 
    413 <div class="org-src-container">
    414 <pre class="src src-go"><span class="org-keyword">func</span> <span class="org-function-name">ANode</span>(<span class="org-rainbow-identifiers-identifier-2">nodeBuilders</span> ...<span class="org-keyword">func</span>(*<span class="org-type">Node</span>)) *<span class="org-type">Node</span> {
    415         <span class="org-rainbow-identifiers-identifier-5">node</span> := &amp;<span class="org-type">Node</span>{
    416                 <span class="org-constant">Name</span>: <span class="org-string">"node"</span>,
    417                 <span class="org-comment-delimiter">// </span><span class="org-comment">Other defaults</span>
    418         }
    419 
    420         <span class="org-keyword">for</span> <span class="org-rainbow-identifiers-identifier-8">_</span>, <span class="org-rainbow-identifiers-identifier-10">build</span> := <span class="org-keyword">range</span> <span class="org-rainbow-identifiers-identifier-2">nodeBuilders</span> {
    421                 <span class="org-function-name">build</span>(<span class="org-rainbow-identifiers-identifier-5">node</span>)
    422         }
    423 
    424         <span class="org-keyword">return</span> <span class="org-rainbow-identifiers-identifier-5">node</span>
    425 }
    426 
    427 <span class="org-keyword">func</span> <span class="org-function-name">APlatform</span>(<span class="org-rainbow-identifiers-identifier-8">platformBuilders</span> ...<span class="org-keyword">func</span>(*<span class="org-type">Platform</span>)) *<span class="org-type">Platform</span> {
    428         <span class="org-rainbow-identifiers-identifier-14">platform</span> := &amp;<span class="org-type">Platform</span>{
    429                 <span class="org-constant">Architecture</span>: <span class="org-string">"x64_86"</span>,
    430                 <span class="org-constant">OS</span>: <span class="org-string">"linux"</span>,
    431         }
    432 
    433         <span class="org-keyword">for</span> <span class="org-rainbow-identifiers-identifier-8">_</span>, <span class="org-rainbow-identifiers-identifier-10">build</span> := <span class="org-keyword">range</span> <span class="org-rainbow-identifiers-identifier-8">platformBuilders</span> {
    434                 <span class="org-function-name">build</span>(<span class="org-rainbow-identifiers-identifier-14">platform</span>)
    435         }
    436 
    437         <span class="org-keyword">return</span> <span class="org-rainbow-identifiers-identifier-14">platform</span>
    438 }
    439 </pre>
    440 </div>
    441 
    442 <p>
    443 And that is it for the actual builder code. It is <b>small</b> and
    444 simple, there is <b>no more <code>NodeBuilder</code></b> struct, and this is highly
    445 extensible. Let's see how to use it.
    446 </p>
    447 
    448 <div class="org-src-container">
    449 <pre class="src src-go"><span class="org-comment-delimiter">// </span><span class="org-comment">a default node</span>
    450 <span class="org-rainbow-identifiers-identifier-2">node1</span> := <span class="org-function-name">ANode</span>()
    451 <span class="org-comment-delimiter">// </span><span class="org-comment">a node with a specific Hostname</span>
    452 <span class="org-rainbow-identifiers-identifier-5">node2</span> := <span class="org-function-name">ANode</span>(<span class="org-keyword">func</span>(<span class="org-rainbow-identifiers-identifier-15">n</span> *<span class="org-type">Node</span>) {
    453         <span class="org-rainbow-identifiers-identifier-15">n</span>.<span class="org-rainbow-identifiers-identifier-10">Hostname</span> = <span class="org-string">"custom-hostname"</span>
    454 })
    455 <span class="org-comment-delimiter">// </span><span class="org-comment">a node with a specific name and platform</span>
    456 <span class="org-rainbow-identifiers-identifier-8">node3</span> := <span class="org-function-name">ANode</span>(<span class="org-keyword">func</span>(<span class="org-rainbow-identifiers-identifier-15">n</span> *<span class="org-type">Node</span>) {
    457         <span class="org-rainbow-identifiers-identifier-15">n</span>.<span class="org-rainbow-identifiers-identifier-8">Name</span> = <span class="org-string">"custom-name"</span>
    458 }, <span class="org-keyword">func</span> (<span class="org-rainbow-identifiers-identifier-15">n</span> *<span class="org-type">Node</span>) {
    459         <span class="org-rainbow-identifiers-identifier-15">n</span>.<span class="org-rainbow-identifiers-identifier-4">Platform</span> = <span class="org-function-name">APlatform</span>(<span class="org-keyword">func</span> (<span class="org-rainbow-identifiers-identifier-10">p</span> *<span class="org-type">Platform</span>) {
    460                 <span class="org-rainbow-identifiers-identifier-10">p</span>.<span class="org-rainbow-identifiers-identifier-12">OS</span> = <span class="org-string">"darwin"</span>
    461         })
    462 })
    463 </pre>
    464 </div>
    465 
    466 <p>
    467 The last step is to define some <i>function builder</i> for common or
    468 widely used customization, to make this <b>expressive</b>. And let
    469 complex, <i>one-time</i> function builder in the end of the user. Now our
    470 tests looks like.
    471 </p>
    472 
    473 <div class="org-src-container">
    474 <pre class="src src-go"><span class="org-keyword">func</span> <span class="org-function-name">TestValidateLinuxIsSupported</span>(<span class="org-rainbow-identifiers-identifier-8">t</span> *<span class="org-type">testing.T</span>) {
    475         <span class="org-rainbow-identifiers-identifier-6">valid</span> := <span class="org-function-name">Validate</span>(<span class="org-function-name">ANode</span>(<span class="org-function-name">WithAPlatform</span>(<span class="org-rainbow-identifiers-identifier-2">Linux</span>)))
    476         <span class="org-keyword">if</span> !<span class="org-rainbow-identifiers-identifier-6">valid</span> {
    477                 <span class="org-rainbow-identifiers-identifier-8">t</span>.<span class="org-function-name">Fatal</span>(<span class="org-string">"linux should be supported, it was not"</span>)
    478         }
    479 }
    480 
    481 <span class="org-keyword">func</span> <span class="org-function-name">TestValidateDarwinIsNotSupported</span>(<span class="org-rainbow-identifiers-identifier-8">t</span> *<span class="org-type">testing.T</span>) {
    482         <span class="org-rainbow-identifiers-identifier-6">valid</span> := <span class="org-function-name">Validate</span>(<span class="org-function-name">ANode</span>(<span class="org-function-name">WithAPlatform</span>(<span class="org-rainbow-identifiers-identifier-15">Darwin</span>)))
    483         <span class="org-keyword">if</span> <span class="org-rainbow-identifiers-identifier-6">valid</span> {
    484                 <span class="org-rainbow-identifiers-identifier-8">t</span>.<span class="org-function-name">Fatal</span>(<span class="org-string">"darwin should not be supported, it was"</span>)
    485         }
    486 }
    487 
    488 <span class="org-comment-delimiter">// </span><span class="org-comment">Function builders</span>
    489 <span class="org-keyword">func</span> <span class="org-function-name">WithAPlatform</span>(<span class="org-rainbow-identifiers-identifier-4">builders</span> ...<span class="org-keyword">func</span>(*<span class="org-type">Platform</span>)) <span class="org-keyword">func</span> (<span class="org-rainbow-identifiers-identifier-15">n</span> *<span class="org-type">Node</span>) {
    490         <span class="org-keyword">return</span> <span class="org-keyword">func</span>(<span class="org-rainbow-identifiers-identifier-15">n</span> *<span class="org-type">Node</span>) {
    491                 <span class="org-rainbow-identifiers-identifier-15">n</span>.<span class="org-rainbow-identifiers-identifier-4">Platform</span> = <span class="org-function-name">Platform</span>(<span class="org-rainbow-identifiers-identifier-4">builders</span>...)
    492         }
    493 }
    494 
    495 <span class="org-keyword">func</span> <span class="org-function-name">Linux</span>(<span class="org-rainbow-identifiers-identifier-10">p</span> *<span class="org-type">Platform</span>) {
    496         <span class="org-rainbow-identifiers-identifier-10">p</span>.<span class="org-rainbow-identifiers-identifier-12">OS</span> = <span class="org-string">"linux"</span>
    497 }
    498 
    499 <span class="org-keyword">func</span> <span class="org-function-name">Darwin</span>(<span class="org-rainbow-identifiers-identifier-10">p</span> *<span class="org-type">Platform</span>) {
    500         <span class="org-rainbow-identifiers-identifier-10">p</span>.<span class="org-rainbow-identifiers-identifier-12">OS</span> = <span class="org-string">"darwin"</span>
    501 }
    502 </pre>
    503 </div>
    504 
    505 <p>
    506 The intent is now clear. It's readable and still resilient to
    507 change. The code <code>Node(WithPlatform(Linux))</code> is easy to understand
    508 for a human. It makes what are the <i>tested</i> characteristics of
    509 <code>struct</code> pretty clear. It's easy to combine multiple builders as the
    510 <code>WithPlatform</code> function shows 👼. It's also easy to create a
    511 <i>function builder</i>, even in a different package (as long as the ways
    512 to modify the struct are exported) and complex or <i>on-off</i> builder
    513 can be embedded in the function call (<code>Node(func(n *Node) { // …
    514   })</code>).
    515 </p>
    516 
    517 <p>
    518 In summary, using these types of builder have several advantages :
    519 </p>
    520 
    521 <ul class="org-ul">
    522 <li>tests are <b>easy to read</b>, and reduce the visual noise</li>
    523 <li>tests are <b>resilient to change</b></li>
    524 <li>builders are <b>easy to compose</b> and very extensible</li>
    525 <li>builders could even be <b>shared</b> with production code as there is
    526 nothing tied to <code>testing</code>.</li>
    527 </ul>
    528 </div>
    529 </div>
    530 
    531       
    532     </article>
    533     <hr />
    534     <div class="prev-next">
    535       
    536       <a class="paging-link prev" href="/posts/2017-04-22-golang-testing-golden-file/" title="Golang testing — golden file">← Previous post</a>
    537       
    538 
    539       
    540       <a class="paging-link next" href="/posts/2016-09-18-firefox-places-and-bookmarks/" title="Firefox hidden feature — places in bookmarks">Next post →</a>
    541       
    542     </div>
    543 
    544   </div>
    545 </div>
    546 
    547 <footer>
    548   <nav>
    549     
    550     <a href="/">home</a>
    551     <span class="text-muted"> | </span>
    552     
    553     <a href="/about">about</a>
    554     <span class="text-muted"> | </span>
    555     
    556     <a href="/archive">archive</a>
    557     <span class="text-muted"> | </span>
    558     
    559     <a href="/categories">categories</a>
    560     <span class="text-muted"> | </span>
    561     
    562     <a href="/tags">tags</a>
    563     <span class="text-muted"> | </span>
    564     
    565     <a href="https://twitter.com/vdemeest">twitter</a>
    566     <span class="text-muted"> | </span>
    567     
    568     <a href="https://github.com/vdemeester">github</a>
    569     <span class="text-muted"> | </span>
    570     
    571     <a href="https://vincent.demeester.fr/index.xml">rss</a>
    572   </nav>
    573   <br/>
    574   <address>
    575     <span class="copyright">
    576       Content and design by Vincent Demeester
    577       (<a rel="licence" href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Some rights reserved</a>)
    578     </span><br />
    579     <span class="engine">
    580       Powered by <a href="https://gohugo.io/">Hugo</a> and <a href="https://github.com/kaushalmodi/ox-hugo/">ox-hugo</a>
    581     </span>
    582   </address>
    583 </footer>
    584 </body>
    585