github.com/System-Glitch/goyave/v2@v2.10.3-0.20200819142921-51011e75d504/docs/guide/advanced/testing.html (about) 1 <!DOCTYPE html> 2 <html lang="en-US"> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="viewport" content="width=device-width,initial-scale=1"> 6 <title>Testing | Goyave</title> 7 <meta name="generator" content="VuePress 1.5.3"> 8 <link rel="icon" type="image/png" sizes="16x16" href="/goyave/goyave_16.png"> 9 <link rel="icon" type="image/png" sizes="32x32" href="/goyave/goyave_32.png"> 10 <link rel="icon" type="image/png" sizes="64x64" href="/goyave/goyave_64.png"> 11 <link rel="icon" type="image/png" sizes="128x128" href="/goyave/goyave_128.png"> 12 <link rel="icon" type="image/png" sizes="256x256" href="/goyave/goyave_256.png"> 13 <link rel="icon" type="image/png" sizes="512x512" href="/goyave/goyave_512.png"> 14 <meta name="description" content="Goyave is a Golang web API framework aiming at cleanliness, fast development and power."> 15 <meta name="og:title" content="Testing - Goyave"> 16 <meta name="twitter:title" content="Testing - Goyave"> 17 <meta name="title" content="Testing - Goyave"> 18 <meta property="twitter:description" content="Goyave is a Golang web API framework aiming at cleanliness, fast development and power."> 19 <meta property="twitter:image:src" content="https://system-glitch.github.io/goyave/goyave_banner.png"> 20 <meta property="twitter:card" content="summary_large_image"> 21 <meta property="og:type" content="website"> 22 <meta property="og:description" content="Goyave is a Golang web API framework aiming at cleanliness, fast development and power."> 23 <meta property="og:image" content="https://system-glitch.github.io/goyave/goyave_banner.png"> 24 <meta property="og:site_name" content="Goyave"> 25 <link rel="preload" href="/goyave/assets/css/0.styles.589fd562.css" as="style"><link rel="preload" href="/goyave/assets/js/app.092490a7.js" as="script"><link rel="preload" href="/goyave/assets/js/4.75a9cc68.js" as="script"><link rel="preload" href="/goyave/assets/js/1.121dd9ed.js" as="script"><link rel="preload" href="/goyave/assets/js/15.36df2a66.js" as="script"><link rel="preload" href="/goyave/assets/js/5.c83f1192.js" as="script"><link rel="prefetch" href="/goyave/assets/js/10.2f07bbf5.js"><link rel="prefetch" href="/goyave/assets/js/11.2d66fdef.js"><link rel="prefetch" href="/goyave/assets/js/12.63171b15.js"><link rel="prefetch" href="/goyave/assets/js/13.770050f3.js"><link rel="prefetch" href="/goyave/assets/js/14.b933d8cf.js"><link rel="prefetch" href="/goyave/assets/js/16.ed66719e.js"><link rel="prefetch" href="/goyave/assets/js/17.7bef5f05.js"><link rel="prefetch" href="/goyave/assets/js/18.470b55ed.js"><link rel="prefetch" href="/goyave/assets/js/19.90e0dab8.js"><link rel="prefetch" href="/goyave/assets/js/20.3a300ca3.js"><link rel="prefetch" href="/goyave/assets/js/21.c3fd6053.js"><link rel="prefetch" href="/goyave/assets/js/22.d5569617.js"><link rel="prefetch" href="/goyave/assets/js/23.931b2034.js"><link rel="prefetch" href="/goyave/assets/js/24.1a4755e7.js"><link rel="prefetch" href="/goyave/assets/js/25.0d463913.js"><link rel="prefetch" href="/goyave/assets/js/26.3c173a7a.js"><link rel="prefetch" href="/goyave/assets/js/27.9c5b36f2.js"><link rel="prefetch" href="/goyave/assets/js/28.41e055b7.js"><link rel="prefetch" href="/goyave/assets/js/29.b87adf4a.js"><link rel="prefetch" href="/goyave/assets/js/3.ef71e77d.js"><link rel="prefetch" href="/goyave/assets/js/6.2336bf0c.js"><link rel="prefetch" href="/goyave/assets/js/7.d60e55c1.js"><link rel="prefetch" href="/goyave/assets/js/8.2ee33a42.js"><link rel="prefetch" href="/goyave/assets/js/9.8e043d60.js"> 26 <link rel="stylesheet" href="/goyave/assets/css/0.styles.589fd562.css"> 27 </head> 28 <body> 29 <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/goyave/" class="home-link router-link-active"><img src="/goyave/goyave_64.png" alt="Goyave" class="logo"> <span class="site-name can-hide">Goyave</span></a> <div class="links"><div class="user-settings"><a title="Dark theme" href="#" class="settings-button"><svg aria-hidden="true" data-prefix="fas" data-icon="cog" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg-inline--fa fa-cog fa-w-16 settings-icon"><path fill="currentColor" d="M8 0c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zM8 15c-3.9 0-7-3.1-7-7 0-2.4 1.2-4.6 3.2-5.9-0.1 0.6-0.2 1.3-0.2 1.9 0 4.9 4 8.9 8.9 9-1.3 1.3-3 2-4.9 2z"></path></svg></a></div> <div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/goyave/guide/" class="nav-link router-link-active"> 30 Guide 31 </a></div><div class="nav-item"><a href="https://pkg.go.dev/github.com/System-Glitch/goyave/v2" target="_blank" rel="noopener noreferrer" class="nav-link external"> 32 pkg.go.dev 33 <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div> <a href="https://github.com/System-Glitch/goyave" target="_blank" rel="noopener noreferrer" class="repo-link"> 34 GitHub 35 <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/goyave/guide/" class="nav-link router-link-active"> 36 Guide 37 </a></div><div class="nav-item"><a href="https://pkg.go.dev/github.com/System-Glitch/goyave/v2" target="_blank" rel="noopener noreferrer" class="nav-link external"> 38 pkg.go.dev 39 <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></div> <a href="https://github.com/System-Glitch/goyave" target="_blank" rel="noopener noreferrer" class="repo-link"> 40 GitHub 41 <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav> <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Guide</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>The Basics</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading open"><span>Advanced</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/goyave/guide/advanced/helpers.html" class="sidebar-link">Helpers</a></li><li><a href="/goyave/guide/advanced/authentication.html" class="sidebar-link">Authentication</a></li><li><a href="/goyave/guide/advanced/localization.html" class="sidebar-link">Localization</a></li><li><a href="/goyave/guide/advanced/testing.html" aria-current="page" class="active sidebar-link">Testing</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/goyave/guide/advanced/testing.html#introduction" class="sidebar-link">Introduction</a></li><li class="sidebar-sub-header"><a href="/goyave/guide/advanced/testing.html#http-tests" class="sidebar-link">HTTP Tests</a></li><li class="sidebar-sub-header"><a href="/goyave/guide/advanced/testing.html#testing-middleware" class="sidebar-link">Testing middleware</a></li><li class="sidebar-sub-header"><a href="/goyave/guide/advanced/testing.html#testsuite-reference" class="sidebar-link">TestSuite reference</a></li><li class="sidebar-sub-header"><a href="/goyave/guide/advanced/testing.html#database-testing" class="sidebar-link">Database testing</a></li></ul></li><li><a href="/goyave/guide/advanced/multi-services.html" class="sidebar-link">Multi-services</a></li><li><a href="/goyave/guide/advanced/cors.html" class="sidebar-link">CORS</a></li><li><a href="/goyave/guide/advanced/status-handlers.html" class="sidebar-link">Status Handlers</a></li><li><a href="/goyave/guide/advanced/logging.html" class="sidebar-link">Logging</a></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="testing"><a href="#testing" class="header-anchor">#</a> Testing <span class="badge tip" style="vertical-align:top;" data-v-15b7b770>Since v2.2.0</span></h1> <p></p><div class="table-of-contents"><ul><li><a href="#introduction">Introduction</a></li><li><a href="#http-tests">HTTP Tests</a><ul><li><a href="#request-and-response">Request and response</a></li><li><a href="#timeout">Timeout</a></li><li><a href="#testing-json-reponses">Testing JSON reponses</a></li><li><a href="#multipart-and-file-upload">Multipart and file upload</a></li></ul></li><li><a href="#testing-middleware">Testing middleware</a></li><li><a href="#testsuite-reference">TestSuite reference</a></li><li><a href="#database-testing">Database testing</a><ul><li><a href="#generators">Generators</a></li><li><a href="#using-factories">Using factories</a></li><li><a href="#seeders">Seeders</a></li></ul></li></ul></div><p></p> <h2 id="introduction"><a href="#introduction" class="header-anchor">#</a> Introduction</h2> <p>Goyave provides an API to ease the unit and functional testing of your application. This API is an extension of <a href="https://github.com/stretchr/testify" target="_blank" rel="noopener noreferrer">testify<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>. <code>goyave.TestSuite</code> inherits from testify's <code>suite.Suite</code>, and sets up the environment for you. That means:</p> <ul><li><code>GOYAVE_ENV</code> environment variable is set to <code>test</code> and restored to its original value when the suite is done.</li> <li>All tests are run using your project's root as working directory. This directory is determined by the presence of a <code>go.mod</code> file.</li> <li>Config and language files are loaded before the tests start. As the environment is set to <code>test</code>, you <strong>need</strong> a <code>config.test.json</code> in the root directory of your project.</li></ul> <p>This setup is done by the function <code>goyave.RunTest</code>, so you shouldn't run your test suites using testify's <code>suite.Run()</code> function.</p> <p>The following example is a <strong>functional</strong> test and would be located in the <code>test</code> package.</p> <div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">import</span> <span class="token punctuation">(</span> 42 <span class="token string">"my-project/http/route"</span> 43 <span class="token string">"github.com/System-Glitch/goyave/v2"</span> 44 <span class="token punctuation">)</span> 45 46 <span class="token keyword">type</span> CustomTestSuite <span class="token keyword">struct</span> <span class="token punctuation">{</span> 47 goyave<span class="token punctuation">.</span>TestSuite 48 <span class="token punctuation">}</span> 49 50 <span class="token keyword">func</span> <span class="token punctuation">(</span>suite <span class="token operator">*</span>CustomTestSuite<span class="token punctuation">)</span> <span class="token function">TestHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 51 suite<span class="token punctuation">.</span><span class="token function">RunServer</span><span class="token punctuation">(</span>route<span class="token punctuation">.</span>Register<span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 52 resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"/hello"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span> 53 suite<span class="token punctuation">.</span><span class="token function">Nil</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> 54 suite<span class="token punctuation">.</span><span class="token function">NotNil</span><span class="token punctuation">(</span>resp<span class="token punctuation">)</span> 55 <span class="token keyword">if</span> resp <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> 56 <span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 57 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">,</span> resp<span class="token punctuation">.</span>StatusCode<span class="token punctuation">)</span> 58 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"Hi!"</span><span class="token punctuation">,</span> <span class="token function">string</span><span class="token punctuation">(</span>suite<span class="token punctuation">.</span><span class="token function">GetBody</span><span class="token punctuation">(</span>resp<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 59 <span class="token punctuation">}</span> 60 <span class="token punctuation">}</span><span class="token punctuation">)</span> 61 <span class="token punctuation">}</span> 62 63 <span class="token keyword">func</span> <span class="token function">TestCustomSuite</span><span class="token punctuation">(</span>t <span class="token operator">*</span>testing<span class="token punctuation">.</span>T<span class="token punctuation">)</span> <span class="token punctuation">{</span> 64 goyave<span class="token punctuation">.</span><span class="token function">RunTest</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> <span class="token function">new</span><span class="token punctuation">(</span>CustomTestSuite<span class="token punctuation">)</span><span class="token punctuation">)</span> 65 <span class="token punctuation">}</span> 66 </code></pre></div><p>We will explain in more details what this test does in the following sections, but in short, this test runs the server, registers all your application routes and executes the second parameter as a server startup hook. The test requests the <code>/hello</code> route with the method <code>GET</code> and checks the content of the response. The server automatically shuts down after the hook is executed and before <code>RunServer</code> returns. See the available assertions in the <a href="https://pkg.go.dev/github.com/stretchr/testify" target="_blank" rel="noopener noreferrer">testify's documentation<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>.</p> <p>This test is a <strong>functional</strong> test. Therefore, it requires route registration and should be located in the <code>test</code> package.</p> <div class="custom-block warning"><p class="custom-block-title">WARNING</p> <p>Because tests using <code>goyave.TestSuite</code> are using the global config, are changing environment variables and working directory and often bind a port, they are <strong>not run in parallel</strong> to avoid conflicts. You don't have to use <code>-p 1</code> in your test command, test suites execution is locked by a mutex.</p></div> <h2 id="http-tests"><a href="#http-tests" class="header-anchor">#</a> HTTP Tests</h2> <p>As shown in the example in the introduction, you can easily run a test server and send requests to it using the <code>suite.RunServer()</code> function.</p> <p>This function takes two parameters.</p> <ul><li>The first is a route registrer function. You should always use your main route registrer to avoid unexpected problems with inherited middleware and route groups.</li> <li>The second parameter is a startup hook for the server, in which you will be running your test procedure.</li></ul> <p>This function is the equivalent of <code>goyave.Start</code>, but doesn't require a <code>goyave.Stop()</code> call: the server stops automatically when the startup hook is finished. All startup hooks are then cleared. The function returns when the server is properly shut down.</p> <p>If you registered startup hooks in your main function, these won't be executed. If you need them for your tests, you need to register them before calling <code>suite.RunServer()</code>.</p> <h3 id="request-and-response"><a href="#request-and-response" class="header-anchor">#</a> Request and response</h3> <p>There are helper functions for the following HTTP methods:</p> <ul><li>GET</li> <li>POST</li> <li>PUT</li> <li>PATCH</li> <li>DELETE</li></ul> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>route string</code></td> <td><code>*http.Response</code></td></tr> <tr><td><code>headers map[string]string</code></td> <td></td></tr> <tr><td><code>body io.Reader</code></td> <td></td></tr></tbody></table> <p><em>Note</em>: The <code>Get</code> function doesn't have a third parameter as GET requests shouldn't have a body. The headers and body are optional, and can be <code>nil</code>.</p> <p>The response body can be retrieved easily using <a href="#suite-getbody"><code>suite.GetBody(response)</code></a>.</p> <div class="language-go extra-class"><pre class="language-go"><code>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"/get"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span> 67 suite<span class="token punctuation">.</span><span class="token function">Nil</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> 68 <span class="token keyword">if</span> err <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> 69 <span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 70 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"response content"</span><span class="token punctuation">,</span> <span class="token function">string</span><span class="token punctuation">(</span>suite<span class="token punctuation">.</span><span class="token function">GetBody</span><span class="token punctuation">(</span>resp<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 71 <span class="token punctuation">}</span> 72 </code></pre></div><h4 id="url-encoded-requests"><a href="#url-encoded-requests" class="header-anchor">#</a> URL-encoded requests</h4> <div class="language-go extra-class"><pre class="language-go"><code>headers <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span><span class="token string">"Content-Type"</span><span class="token punctuation">:</span> <span class="token string">"application/x-www-form-urlencoded; param=value"</span><span class="token punctuation">}</span> 73 resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">Post</span><span class="token punctuation">(</span><span class="token string">"/product"</span><span class="token punctuation">,</span> headers<span class="token punctuation">,</span> strings<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span><span class="token string">"field=value"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 74 suite<span class="token punctuation">.</span><span class="token function">Nil</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> 75 <span class="token keyword">if</span> err <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> 76 <span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 77 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"response content"</span><span class="token punctuation">,</span> <span class="token function">string</span><span class="token punctuation">(</span>suite<span class="token punctuation">.</span><span class="token function">GetBody</span><span class="token punctuation">(</span>resp<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 78 <span class="token punctuation">}</span> 79 </code></pre></div><h4 id="json-requests"><a href="#json-requests" class="header-anchor">#</a> JSON requests</h4> <div class="language-go extra-class"><pre class="language-go"><code>headers <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span><span class="token string">"Content-Type"</span><span class="token punctuation">:</span> <span class="token string">"application/json"</span><span class="token punctuation">}</span> 80 body<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">Marshal</span><span class="token punctuation">(</span><span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token string">"name"</span><span class="token punctuation">:</span> <span class="token string">"Pizza"</span><span class="token punctuation">,</span> <span class="token string">"price"</span><span class="token punctuation">:</span> <span class="token number">12.5</span><span class="token punctuation">}</span><span class="token punctuation">)</span> 81 resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">Post</span><span class="token punctuation">(</span><span class="token string">"/product"</span><span class="token punctuation">,</span> headers<span class="token punctuation">,</span> bytes<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>body<span class="token punctuation">)</span><span class="token punctuation">)</span> 82 suite<span class="token punctuation">.</span><span class="token function">Nil</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> 83 <span class="token keyword">if</span> err <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> 84 <span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 85 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"response content"</span><span class="token punctuation">,</span> <span class="token function">string</span><span class="token punctuation">(</span>suite<span class="token punctuation">.</span><span class="token function">GetBody</span><span class="token punctuation">(</span>resp<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 86 <span class="token punctuation">}</span> 87 </code></pre></div><div class="custom-block tip"><p class="custom-block-title">TIP</p> <p>If you need to test another method, you can use the <a href="#testsuite-request"><code>suite.Request()</code></a> function.</p></div> <h3 id="timeout"><a href="#timeout" class="header-anchor">#</a> Timeout</h3> <p><code>goyave.TestSuite</code> has a default timeout value of <strong>5 seconds</strong>. This timeout is used for the <code>RunServer</code> function as well as for the request functions(<code>Get</code>, <code>Post</code>, etc.). If the timeout expires, the test fails. This prevents your test from freezing if something goes wrong.</p> <p>The timeout can be modified as needed using <code>suite.SetTimeout()</code>:</p> <div class="language-go extra-class"><pre class="language-go"><code>suite<span class="token punctuation">.</span><span class="token function">SetTimeout</span><span class="token punctuation">(</span><span class="token number">10</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span> 88 </code></pre></div><h3 id="testing-json-reponses"><a href="#testing-json-reponses" class="header-anchor">#</a> Testing JSON reponses</h3> <p>It is very likely that you will need to check the content of a JSON response when testing your application. Instead of unmarshaling the JSON yourself, Goyave provides the <a href="#suite-getjsonbody"><code>suite.GetJSONBody</code></a> function. This function decodes the raw body of the request. If the data cannot be decoded, or is invalid JSON, the test fails and the function returns <code>nil</code>.</p> <div class="language-go extra-class"><pre class="language-go"><code>suite<span class="token punctuation">.</span><span class="token function">RunServer</span><span class="token punctuation">(</span>route<span class="token punctuation">.</span>Register<span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 89 resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"/product"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span> 90 suite<span class="token punctuation">.</span><span class="token function">Nil</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> 91 <span class="token keyword">if</span> err <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> 92 <span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 93 json <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token punctuation">}</span> 94 err <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">GetJSONBody</span><span class="token punctuation">(</span>resp<span class="token punctuation">,</span> <span class="token operator">&</span>json<span class="token punctuation">)</span> 95 suite<span class="token punctuation">.</span><span class="token function">Nil</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> 96 <span class="token keyword">if</span> err <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token comment">// You should always check parsing error before continuing.</span> 97 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"value"</span><span class="token punctuation">,</span> json<span class="token punctuation">[</span><span class="token string">"field"</span><span class="token punctuation">]</span><span class="token punctuation">)</span> 98 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token function">float64</span><span class="token punctuation">(</span><span class="token number">42</span><span class="token punctuation">)</span><span class="token punctuation">,</span> json<span class="token punctuation">[</span><span class="token string">"number"</span><span class="token punctuation">]</span><span class="token punctuation">)</span> 99 <span class="token punctuation">}</span> 100 <span class="token punctuation">}</span> 101 <span class="token punctuation">}</span><span class="token punctuation">)</span> 102 </code></pre></div><h3 id="multipart-and-file-upload"><a href="#multipart-and-file-upload" class="header-anchor">#</a> Multipart and file upload</h3> <p>You may need to test requests requiring file uploads. The best way to do this is using Go's <code>multipart.Writer</code>. Goyave provides functions to help you create a Multipart form.</p> <div class="language-go extra-class"><pre class="language-go"><code>suite<span class="token punctuation">.</span><span class="token function">RunServer</span><span class="token punctuation">(</span>route<span class="token punctuation">.</span>Register<span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 103 <span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token string">"profile.png"</span> 104 body <span class="token operator">:=</span> <span class="token operator">&</span>bytes<span class="token punctuation">.</span>Buffer<span class="token punctuation">{</span><span class="token punctuation">}</span> 105 writer <span class="token operator">:=</span> multipart<span class="token punctuation">.</span><span class="token function">NewWriter</span><span class="token punctuation">(</span>body<span class="token punctuation">)</span> 106 suite<span class="token punctuation">.</span><span class="token function">WriteField</span><span class="token punctuation">(</span>writer<span class="token punctuation">,</span> <span class="token string">"email"</span><span class="token punctuation">,</span> <span class="token string">"johndoe@example.org"</span><span class="token punctuation">)</span> 107 suite<span class="token punctuation">.</span><span class="token function">WriteFile</span><span class="token punctuation">(</span>writer<span class="token punctuation">,</span> path<span class="token punctuation">,</span> <span class="token string">"profile_picture"</span><span class="token punctuation">,</span> filepath<span class="token punctuation">.</span><span class="token function">Base</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">)</span> 108 <span class="token keyword">if</span> err <span class="token operator">:=</span> writer<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> 109 <span class="token function">panic</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> 110 <span class="token punctuation">}</span> 111 112 <span class="token comment">// Don't forget to set the "Content-Type" header!</span> 113 headers <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span><span class="token string">"Content-Type"</span><span class="token punctuation">:</span> writer<span class="token punctuation">.</span><span class="token function">FormDataContentType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span> 114 115 resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">Post</span><span class="token punctuation">(</span><span class="token string">"/register"</span><span class="token punctuation">,</span> headers<span class="token punctuation">,</span> body<span class="token punctuation">)</span> 116 suite<span class="token punctuation">.</span><span class="token function">Nil</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> 117 <span class="token keyword">if</span> err <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> 118 <span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 119 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"Welcome!"</span><span class="token punctuation">,</span> <span class="token function">string</span><span class="token punctuation">(</span>suite<span class="token punctuation">.</span><span class="token function">GetBody</span><span class="token punctuation">(</span>resp<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 120 <span class="token punctuation">}</span> 121 <span class="token punctuation">}</span><span class="token punctuation">)</span> 122 </code></pre></div><div class="custom-block tip"><p class="custom-block-title">TIP</p> <p>You can write a multi-file upload by calling <code>suite.WriteFile</code> successively using the same field name.</p></div> <h2 id="testing-middleware"><a href="#testing-middleware" class="header-anchor">#</a> Testing middleware</h2> <p>You can unit-test middleware using the <a href="#suite-middleware"><code>suite.Middleware</code></a> function. This function passes a <code>*goyave.Request</code> to your middlware and returns the <code>*http.Response</code>. This function also takes a test procedure function as a parameter. This function will simulate a <strong>controller handler</strong>, so you can test if the middleware alters the request.</p> <div class="language-go extra-class"><pre class="language-go"><code>rawRequest <span class="token operator">:=</span> httptest<span class="token punctuation">.</span><span class="token function">NewRequest</span><span class="token punctuation">(</span><span class="token string">"GET"</span><span class="token punctuation">,</span> <span class="token string">"/test-route"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span> 123 rawRequest<span class="token punctuation">.</span>Header<span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span><span class="token string">"Content-Type"</span><span class="token punctuation">,</span> <span class="token string">"application/json"</span><span class="token punctuation">)</span> 124 request <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">CreateTestRequest</span><span class="token punctuation">(</span>rawRequest<span class="token punctuation">)</span> 125 request<span class="token punctuation">.</span>Data <span class="token operator">=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token string">"text"</span><span class="token punctuation">:</span> <span class="token string">" \n test \t"</span><span class="token punctuation">}</span> 126 127 result <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">Middleware</span><span class="token punctuation">(</span>middleware<span class="token punctuation">.</span>Trim<span class="token punctuation">,</span> request<span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>response <span class="token operator">*</span>Response<span class="token punctuation">,</span> request <span class="token operator">*</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span> 128 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"application/json"</span><span class="token punctuation">,</span> request<span class="token punctuation">.</span><span class="token function">Header</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"Content-Type"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 129 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token string">"test"</span><span class="token punctuation">,</span> request<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span><span class="token string">"text"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 130 <span class="token punctuation">}</span><span class="token punctuation">)</span> 131 132 suite<span class="token punctuation">.</span><span class="token function">Equal</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">,</span> result<span class="token punctuation">.</span>StatusCode<span class="token punctuation">)</span> 133 </code></pre></div><p>If you want to test a blocking middleware, flag the test as failed in the test procedure. Indeed, the procedure shouldn't be executed if your middleware doesn't pass to the next handler.</p> <div class="language-go extra-class"><pre class="language-go"><code>request <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">CreateTestRequest</span><span class="token punctuation">(</span><span class="token boolean">nil</span><span class="token punctuation">)</span> 134 suite<span class="token punctuation">.</span><span class="token function">Middleware</span><span class="token punctuation">(</span>middleware<span class="token punctuation">.</span>Auth<span class="token punctuation">,</span> request<span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>response <span class="token operator">*</span>Response<span class="token punctuation">,</span> request <span class="token operator">*</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span> 135 suite<span class="token punctuation">.</span><span class="token function">Fail</span><span class="token punctuation">(</span><span class="token string">"Auth middleware passed"</span><span class="token punctuation">)</span> 136 <span class="token punctuation">}</span><span class="token punctuation">)</span> 137 </code></pre></div><h2 id="testsuite-reference"><a href="#testsuite-reference" class="header-anchor">#</a> TestSuite reference</h2> <div class="table"><p><a href="#testsuite-runserver">RunServer</a> <a href="#testsuite-timeout">Timeout</a> <a href="#testsuite-settimeout">SetTimeout</a> <a href="#testsuite-middleware">Middleware</a> <a href="#testsuite-get">Get</a> <a href="#testsuite-post">Post</a> <a href="#testsuite-put">Put</a> <a href="#testsuite-patch">Patch</a> <a href="#testsuite-delete">Delete</a> <a href="#testsuite-request">Request</a> <a href="#testsuite-getbody">GetBody</a> <a href="#testsuite-getjsonbody">GetJSONBody</a> <a href="#testsuite-createtestfiles">CreateTestFiles</a> <a href="#testsuite-createtestrequest">CreateTestRequest</a> <a href="#testsuite-createtestresponse">CreateTestResponse</a> <a href="#testsuite-createtestresponsewithrequest">CreateTestResponseWithRequest</a> <a href="#testsuite-writefile">WriteFile</a> <a href="#testsuite-writefield">WriteField</a> <a href="#testsuite-cleardatabase">ClearDatabase</a> <a href="#testsuite-cleardatabasetables">ClearDatabaseTables</a> <a href="#goyave-runtest">RunTest</a></p></div><h4 id="testsuite-runserver"><a href="#testsuite-runserver" class="header-anchor">#</a> TestSuite.RunServer</h4> <p>RunServer start the application and run the given functional test procedure.</p> <p>This function is the equivalent of <code>goyave.Start()</code>.<br> 138 The test fails if the suite's timeout is exceeded.<br> 139 The server automatically shuts down when the function ends.<br> 140 This function is synchronized, that means that the server is properly stopped when the function returns.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>routeRegistrer func(*goyave.Router)</code></td> <td><code>void</code></td></tr> <tr><td><code>procedure func()</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-timeout"><a href="#testsuite-timeout" class="header-anchor">#</a> TestSuite.Timeout</h4> <p>Get the timeout for test failure when using RunServer or requests.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td></td> <td><code>time.Duration</code></td></tr></tbody></table> <h4 id="testsuite-settimeout"><a href="#testsuite-settimeout" class="header-anchor">#</a> TestSuite.SetTimeout</h4> <p>Set the timeout for test failure when using RunServer or requests.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>time.Duration</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-middleware"><a href="#testsuite-middleware" class="header-anchor">#</a> TestSuite.Middleware</h4> <p>Executes the given middleware and returns the HTTP response. Core middleware (recovery, parsing and language) is not executed.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>middleware goyave.Middleware</code></td> <td><code>*http.Response</code></td></tr> <tr><td><code>request *goyave.Request</code></td> <td></td></tr> <tr><td><code>procedure goyave.Handler</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-get"><a href="#testsuite-get" class="header-anchor">#</a> TestSuite.Get</h4> <p>Execute a GET request on the given route. Headers are optional.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>route string</code></td> <td><code>*http.Response</code></td></tr> <tr><td><code>headers map[string]string</code></td> <td><code>error</code></td></tr></tbody></table> <h4 id="testsuite-post"><a href="#testsuite-post" class="header-anchor">#</a> TestSuite.Post</h4> <p>Execute a POST request on the given route. Headers and body are optional.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>route string</code></td> <td><code>*http.Response</code></td></tr> <tr><td><code>headers map[string]string</code></td> <td><code>error</code></td></tr> <tr><td><code>body io.Reader</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-put"><a href="#testsuite-put" class="header-anchor">#</a> TestSuite.Put</h4> <p>Execute a PUT request on the given route. Headers and body are optional.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>route string</code></td> <td><code>*http.Response</code></td></tr> <tr><td><code>headers map[string]string</code></td> <td><code>error</code></td></tr> <tr><td><code>body io.Reader</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-patch"><a href="#testsuite-patch" class="header-anchor">#</a> TestSuite.Patch</h4> <p>Execute a PATCH request on the given route. Headers and body are optional.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>route string</code></td> <td><code>*http.Response</code></td></tr> <tr><td><code>headers map[string]string</code></td> <td><code>error</code></td></tr> <tr><td><code>body io.Reader</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-delete"><a href="#testsuite-delete" class="header-anchor">#</a> TestSuite.Delete</h4> <p>Execute a DELETE request on the given route. Headers and body are optional.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>route string</code></td> <td><code>*http.Response</code></td></tr> <tr><td><code>headers map[string]string</code></td> <td><code>error</code></td></tr> <tr><td><code>body io.Reader</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-request"><a href="#testsuite-request" class="header-anchor">#</a> TestSuite.Request</h4> <p>Execute a request on the given route. Headers and body are optional.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>method string</code></td> <td><code>*http.Response</code></td></tr> <tr><td><code>route string</code></td> <td><code>error</code></td></tr> <tr><td><code>headers map[string]string</code></td> <td></td></tr> <tr><td><code>body io.Reader</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-getbody"><a href="#testsuite-getbody" class="header-anchor">#</a> TestSuite.GetBody</h4> <p>Read the whole body of a response. If read failed, test fails and return empty byte slice.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>response *http.Response</code></td> <td><code>[]byte</code></td></tr></tbody></table> <h4 id="testsuite-getjsonbody"><a href="#testsuite-getjsonbody" class="header-anchor">#</a> TestSuite.GetJSONBody</h4> <p>Read the whole body of a response and decode it as JSON. If read or decode failed, test fails. The <code>data</code> parameter should be a pointer.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>response *http.Response</code></td> <td><code>error</code></td></tr> <tr><td><code>data interface{}</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-createtestfiles"><a href="#testsuite-createtestfiles" class="header-anchor">#</a> TestSuite.CreateTestFiles</h4> <p>Create a slice of <code>filesystem.File</code> from the given paths. Files are passed to a temporary http request and parsed as Multipart form, to reproduce the way files are obtained in real scenarios.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>paths ...string</code></td> <td><code>[]filesystem.File</code></td></tr></tbody></table> <h4 id="testsuite-createtestrequest"><a href="#testsuite-createtestrequest" class="header-anchor">#</a> TestSuite.CreateTestRequest</h4> <p>Create a <code>*goyave.Request</code> from the given raw request. This function is aimed at making it easier to unit test Requests.</p> <p>If passed request is <code>nil</code>, a default <code>GET</code> request to <code>/</code> is used.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>rawRequest *http.Request</code></td> <td><code>*goyave.Request</code></td></tr></tbody></table> <p><strong>Example:</strong></p> <div class="language-go extra-class"><pre class="language-go"><code>rawRequest <span class="token operator">:=</span> httptest<span class="token punctuation">.</span><span class="token function">NewRequest</span><span class="token punctuation">(</span><span class="token string">"GET"</span><span class="token punctuation">,</span> <span class="token string">"/test-route"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span> 141 rawRequest<span class="token punctuation">.</span>Header<span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span><span class="token string">"Content-Type"</span><span class="token punctuation">,</span> <span class="token string">"application/json"</span><span class="token punctuation">)</span> 142 request <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">CreateTestRequest</span><span class="token punctuation">(</span>rawRequest<span class="token punctuation">)</span> 143 request<span class="token punctuation">.</span>Lang <span class="token operator">=</span> <span class="token string">"en-US"</span> 144 request<span class="token punctuation">.</span>Data <span class="token operator">=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token string">"field"</span><span class="token punctuation">:</span> <span class="token string">"value"</span><span class="token punctuation">}</span> 145 </code></pre></div><h4 id="testsuite-createtestresponse"><a href="#testsuite-createtestresponse" class="header-anchor">#</a> TestSuite.CreateTestResponse</h4> <p>Create an empty response with the given response writer. This function is aimed at making it easier to unit test Responses.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>recorder http.ResponseWriter</code></td> <td><code>*goyave.Response</code></td></tr></tbody></table> <p><strong>Example:</strong></p> <div class="language-go extra-class"><pre class="language-go"><code>writer <span class="token operator">:=</span> httptest<span class="token punctuation">.</span><span class="token function">NewRecorder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 146 response <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">CreateTestResponse</span><span class="token punctuation">(</span>writer<span class="token punctuation">)</span> 147 response<span class="token punctuation">.</span><span class="token function">Status</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusNoContent<span class="token punctuation">)</span> 148 result <span class="token operator">:=</span> writer<span class="token punctuation">.</span><span class="token function">Result</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 149 fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span>StatusCode<span class="token punctuation">)</span> <span class="token comment">// 204</span> 150 </code></pre></div><h4 id="testsuite-createtestresponsewithrequest"><a href="#testsuite-createtestresponsewithrequest" class="header-anchor">#</a> TestSuite.CreateTestResponseWithRequest</h4> <p>Create an empty response with the given response writer and HTTP request. This function is aimed at making it easier to unit test Responses needing the raw request's information, such as redirects.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>recorder http.ResponseWriter</code></td> <td><code>*goyave.Response</code></td></tr> <tr><td><code>rawRequest *http.Request</code></td> <td></td></tr></tbody></table> <p><strong>Example:</strong></p> <div class="language-go extra-class"><pre class="language-go"><code>writer <span class="token operator">:=</span> httptest<span class="token punctuation">.</span><span class="token function">NewRecorder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 151 rawRequest <span class="token operator">:=</span> httptest<span class="token punctuation">.</span><span class="token function">NewRequest</span><span class="token punctuation">(</span><span class="token string">"POST"</span><span class="token punctuation">,</span> <span class="token string">"/test-route"</span><span class="token punctuation">,</span> strings<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span><span class="token string">"body"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 152 response <span class="token operator">:=</span> suite<span class="token punctuation">.</span><span class="token function">CreateTestResponseWithRequest</span><span class="token punctuation">(</span>writer<span class="token punctuation">,</span> rawRequest<span class="token punctuation">)</span> 153 response<span class="token punctuation">.</span><span class="token function">Status</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusNoContent<span class="token punctuation">)</span> 154 result <span class="token operator">:=</span> writer<span class="token punctuation">.</span><span class="token function">Result</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 155 fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span>StatusCode<span class="token punctuation">)</span> <span class="token comment">// 204</span> 156 </code></pre></div><h4 id="testsuite-writefile"><a href="#testsuite-writefile" class="header-anchor">#</a> TestSuite.WriteFile</h4> <p>Write a file to the given writer. This function is handy for file upload testing. The test fails if an error occurred.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>write *multipart.Writer</code></td> <td><code>void</code></td></tr> <tr><td><code>path string</code></td> <td></td></tr> <tr><td><code>fieldName string</code></td> <td></td></tr> <tr><td><code>fileName string</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-writefield"><a href="#testsuite-writefield" class="header-anchor">#</a> TestSuite.WriteField</h4> <p>Create and write a new multipart form field. The test fails if the field couldn't be written.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>write *multipart.Writer</code></td> <td><code>void</code></td></tr> <tr><td><code>fieldName string</code></td> <td></td></tr> <tr><td><code>value string</code></td> <td></td></tr></tbody></table> <h4 id="testsuite-cleardatabase"><a href="#testsuite-cleardatabase" class="header-anchor">#</a> TestSuite.ClearDatabase</h4> <p>Delete all records in all tables. This function only clears the tables of registered models.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td></td> <td><code>void</code></td></tr></tbody></table> <h4 id="testsuite-cleardatabasetables"><a href="#testsuite-cleardatabasetables" class="header-anchor">#</a> TestSuite.ClearDatabaseTables</h4> <p>Drop all tables. This function only clears the tables of registered models.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td></td> <td><code>void</code></td></tr></tbody></table> <h4 id="goyave-runtest"><a href="#goyave-runtest" class="header-anchor">#</a> goyave.RunTest</h4> <p>Run a test suite with prior initialization of a test environment. The GOYAVE_ENV environment variable is automatically set to "test" and restored to its original value at the end of the test run.</p> <p>All tests are run using your project's root as working directory. This directory is determined by the presence of a <code>go.mod</code> file.</p> <p>The function returns true if the test passed.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>t *testing.T</code></td> <td><code>bool</code></td></tr> <tr><td><code>suite ITestSuite</code></td> <td></td></tr></tbody></table> <div class="custom-block tip"><p class="custom-block-title">TIP</p> <p><code>ITestSuite</code> is the interface <code>TestSuite</code> is implementing.</p></div> <h2 id="database-testing"><a href="#database-testing" class="header-anchor">#</a> Database testing</h2> <p>You may need to test features interacting with your database. Goyave provides a handy way to generate and save records in your database: <strong>factories</strong>.</p> <p><strong>All registered models records are automatically deleted from the database when each test suite completes.</strong></p> <p>It is a good practice to use a separate database dedicated for testing, named <code>myapp_test</code> for example. Don't forget to change the database information in your <code>config.test.json</code> file.</p> <p>All functions below require the <code>database</code>package to be imported.</p> <div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">import</span> <span class="token string">"github.com/System-Glitch/goyave/v2/database"</span> 157 </code></pre></div><div class="custom-block tip"><p class="custom-block-title">TIP</p> <p>You may want to use a clean database for each of your tests. You can clear your database before each test using <a href="https://pkg.go.dev/github.com/stretchr/testify/suite?tab=doc#SetupTestSuite" target="_blank" rel="noopener noreferrer"><code>suite.SetupTest()</code><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>.</p> <div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">func</span> <span class="token punctuation">(</span>suite <span class="token operator">*</span>CustomTestSuite<span class="token punctuation">)</span> <span class="token function">SetupTest</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 158 suite<span class="token punctuation">.</span><span class="token function">ClearDatabase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 159 <span class="token punctuation">}</span> 160 </code></pre></div></div> <h3 id="generators"><a href="#generators" class="header-anchor">#</a> Generators</h3> <p>Factories need a <strong>generator function</strong>. These functions generate a single random record. You can use the faking library of your choice, but in this example we are going to use <a href="https://github.com/bxcodec/faker" target="_blank" rel="noopener noreferrer"><code>github.com/bxcodec/faker</code><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>.</p> <div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">import</span> <span class="token string">"github.com/bxcodec/faker/v3"</span> 161 162 <span class="token keyword">func</span> <span class="token function">UserGenerator</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">{</span> 163 user <span class="token operator">:=</span> <span class="token operator">&</span>User<span class="token punctuation">{</span><span class="token punctuation">}</span> 164 user<span class="token punctuation">.</span>Name <span class="token operator">=</span> faker<span class="token punctuation">.</span><span class="token function">Name</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 165 166 faker<span class="token punctuation">.</span><span class="token function">SetGenerateUniqueValues</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> 167 user<span class="token punctuation">.</span>Email <span class="token operator">=</span> faker<span class="token punctuation">.</span><span class="token function">Email</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 168 faker<span class="token punctuation">.</span><span class="token function">SetGenerateUniqueValues</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span> 169 <span class="token keyword">return</span> user 170 <span class="token punctuation">}</span> 171 </code></pre></div><div class="custom-block tip"><p class="custom-block-title">TIP</p> <ul><li><code>database.Generator</code> is an alias for <code>func() interface{}</code>.</li> <li>Generator functions should be declared in the same file as the model it is generating.</li></ul></div> <p>Generators can also create associated records. Associated records should be generated using their respective generators. In the following example, we are generating users for an application allowing users to write blog posts.</p> <div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">func</span> <span class="token function">UserGenerator</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">{</span> 172 user <span class="token operator">:=</span> <span class="token operator">&</span>User<span class="token punctuation">{</span><span class="token punctuation">}</span> 173 <span class="token comment">// ... Generate users fields ...</span> 174 175 <span class="token comment">// Generate between 0 and 10 blog posts</span> 176 rand<span class="token punctuation">.</span><span class="token function">Seed</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">UnixNano</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 177 user<span class="token punctuation">.</span>Posts <span class="token operator">=</span> database<span class="token punctuation">.</span><span class="token function">NewFactory</span><span class="token punctuation">(</span>PostGenerator<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Generate</span><span class="token punctuation">(</span>rand<span class="token punctuation">.</span><span class="token function">Intn</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span> 178 179 <span class="token keyword">return</span> user 180 <span class="token punctuation">}</span> 181 </code></pre></div><h3 id="using-factories"><a href="#using-factories" class="header-anchor">#</a> Using factories</h3> <p>You can create a factory from any <code>database.Generator</code>.</p> <div class="language-go extra-class"><pre class="language-go"><code>factory <span class="token operator">:=</span> database<span class="token punctuation">.</span><span class="token function">NewFactory</span><span class="token punctuation">(</span>model<span class="token punctuation">.</span>UserGenerator<span class="token punctuation">)</span> 182 183 <span class="token comment">// Generate 5 random users</span> 184 records <span class="token operator">:=</span> factory<span class="token punctuation">.</span><span class="token function">Generate</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span> 185 186 <span class="token comment">// Generate and insert 5 random users into the database</span> 187 insertedRecords <span class="token operator">:=</span> factory<span class="token punctuation">.</span><span class="token function">Save</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span> 188 </code></pre></div><p>Note that generated records will not have an ID if they are not inserted into the database.</p> <p>Associated records created by the generator will also be inserted on <code>factory.Save</code>.</p> <h4 id="overrides"><a href="#overrides" class="header-anchor">#</a> Overrides</h4> <p>It is possible to override some of the generated data if needed, for example if you need to test the behavior of a function with a specific value. All generated structures will be merged with the override.</p> <div class="language-go extra-class"><pre class="language-go"><code>override <span class="token operator">:=</span> <span class="token operator">&</span>model<span class="token punctuation">.</span>User<span class="token punctuation">{</span> 189 Name<span class="token punctuation">:</span> <span class="token string">"Jérémy"</span><span class="token punctuation">,</span> 190 <span class="token punctuation">}</span> 191 records <span class="token operator">:=</span> factory<span class="token punctuation">.</span><span class="token function">Override</span><span class="token punctuation">(</span>override<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Generate</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span> 192 <span class="token comment">// All generated records will have the same name: "Jérémy"</span> 193 </code></pre></div><div class="custom-block warning"><p class="custom-block-title">WARNING</p> <p>Overrides must be of the <strong>same type</strong> as the generated record.</p></div> <h4 id="factory-reference"><a href="#factory-reference" class="header-anchor">#</a> Factory reference</h4> <div class="table"><p><a href="#database-newfactory">NewFactory</a> <a href="#factory-override">Override</a></p></div><h4 id="database-newfactory"><a href="#database-newfactory" class="header-anchor">#</a> database.NewFactory</h4> <p>Create a new Factory. The given generator function will be used to generate records.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>generator database.Generator</code></td> <td><code>database.Factory</code></td></tr></tbody></table> <h4 id="factory-override"><a href="#factory-override" class="header-anchor">#</a> Factory.Override</h4> <p>Set an override model for generated records. Values present in the override model will replace the ones in the generated records. This function expects a struct <strong>pointer</strong> as parameter. This function returns the same instance of <code>Factory</code> so this method can be chained.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>override interface{}</code></td> <td><code>database.Factory</code></td></tr></tbody></table> <h4 id="factory-generate"><a href="#factory-generate" class="header-anchor">#</a> Factory.Generate</h4> <p>Generate a number of records using the given factory.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>count uint</code></td> <td><code>[]interface{}</code></td></tr></tbody></table> <h4 id="factory-save"><a href="#factory-save" class="header-anchor">#</a> Factory.Save</h4> <p>Generate a number of records using the given factory and return the inserted records.</p> <table><thead><tr><th>Parameters</th> <th>Return</th></tr></thead> <tbody><tr><td><code>count uint</code></td> <td><code>[]interface{}</code></td></tr></tbody></table> <h3 id="seeders"><a href="#seeders" class="header-anchor">#</a> Seeders</h3> <p>Seeders are functions which create a number of random records in the database in order to create a full and realistic test environment. Seeders are written in the <code>database/seeder</code> package.</p> <p>Each seeder should have its own file. A seeder's responsibilities are limited to a single table or model. For example, the <code>seeder.User</code> should only seed the <code>users</code> table. Moreover, seeders should have the same name as the model they are using.</p> <div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">package</span> seeder 194 195 <span class="token keyword">import</span> <span class="token punctuation">(</span> 196 <span class="token string">"my-project/database/model"</span> 197 <span class="token string">"github.com/System-Glitch/goyave/v2/database"</span> 198 <span class="token punctuation">)</span> 199 200 <span class="token keyword">func</span> <span class="token function">User</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 201 database<span class="token punctuation">.</span><span class="token function">NewFactory</span><span class="token punctuation">(</span>model<span class="token punctuation">.</span>UserGenerator<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Save</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span> 202 <span class="token punctuation">}</span> 203 </code></pre></div></div> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/System-Glitch/goyave/edit/master/docs_src/src/guide/advanced/testing.md" target="_blank" rel="noopener noreferrer">Edit this page on GitHub</a> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></div> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev"> 204 ← 205 <a href="/goyave/guide/advanced/localization.html" class="prev"> 206 Localization 207 </a></span> <span class="next"><a href="/goyave/guide/advanced/multi-services.html"> 208 Multi-services 209 </a> 210 → 211 </span></p></div> </main></div><div class="global-ui"><!----></div></div> 212 <script src="/goyave/assets/js/app.092490a7.js" defer></script><script src="/goyave/assets/js/4.75a9cc68.js" defer></script><script src="/goyave/assets/js/1.121dd9ed.js" defer></script><script src="/goyave/assets/js/15.36df2a66.js" defer></script><script src="/goyave/assets/js/5.c83f1192.js" defer></script> 213 </body> 214 </html>