foomo-docs/blog/go-race-conditions-testing-and-coverage.html
2023-05-10 09:42:11 +00:00

21 lines
28 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" dir="ltr" class="blog-wrapper blog-post-page plugin-blog plugin-id-default">
<head>
<meta charset="UTF-8">
<meta name="generator" content="Docusaurus v2.4.0">
<title data-rh="true">Go race conditions testing and coverage | foomo project docs</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:url" content="https://www.foomo.org/blog/go-race-conditions-testing-and-coverage"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docusaurus_tag" content="default"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docsearch:docusaurus_tag" content="default"><meta data-rh="true" property="og:title" content="Go race conditions testing and coverage | foomo project docs"><meta data-rh="true" name="description" content="Go has extensive support for concurrency through goroutines and channels. This feature allows programs to progress on several tasks at the same time but it requires some extra care to prevent situations where multiple goroutines might collide and lead to a panic. These are known as race conditions and they happen when a shared variable is read and written at the same time by two different routines. A typical example is a concurrent read/write of a map in memory."><meta data-rh="true" property="og:description" content="Go has extensive support for concurrency through goroutines and channels. This feature allows programs to progress on several tasks at the same time but it requires some extra care to prevent situations where multiple goroutines might collide and lead to a panic. These are known as race conditions and they happen when a shared variable is read and written at the same time by two different routines. A typical example is a concurrent read/write of a map in memory."><meta data-rh="true" property="og:type" content="article"><meta data-rh="true" property="article:published_time" content="2023-03-17T00:00:00.000Z"><meta data-rh="true" property="article:author" content="https://github.com/cvidmar,https://github.com/dreadl0ck"><meta data-rh="true" property="article:tag" content="golang,concurrency,testing"><link data-rh="true" rel="icon" href="/img/favicon.ico"><link data-rh="true" rel="canonical" href="https://www.foomo.org/blog/go-race-conditions-testing-and-coverage"><link data-rh="true" rel="alternate" href="https://www.foomo.org/blog/go-race-conditions-testing-and-coverage" hreflang="en"><link data-rh="true" rel="alternate" href="https://www.foomo.org/blog/go-race-conditions-testing-and-coverage" hreflang="x-default"><link data-rh="true" rel="preconnect" href="https://SUATUVZDDM-dsn.algolia.net" crossorigin="anonymous"><link rel="alternate" type="application/rss+xml" href="/blog/rss.xml" title="foomo project docs RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/blog/atom.xml" title="foomo project docs Atom Feed">
<link rel="search" type="application/opensearchdescription+xml" title="foomo project docs" href="/opensearch.xml"><link rel="stylesheet" href="/assets/css/styles.45fe17b6.css">
<link rel="preload" href="/assets/js/runtime~main.24c19dd7.js" as="script">
<link rel="preload" href="/assets/js/main.4cf1833b.js" as="script">
</head>
<body class="navigation-with-keyboard">
<script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}return t}()||function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"light")}()</script><div id="__docusaurus">
<div role="region" aria-label="Skip to main content"><a class="skipToContent_fXgn" href="#docusaurus_skipToContent_fallback">Skip to main content</a></div><nav aria-label="Main" class="navbar navbar--fixed-top"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Toggle navigation bar" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/"><b class="navbar__title text--truncate">foomo</b></a><a class="navbar__item navbar__link" href="/docs/general">General</a><a class="navbar__item navbar__link" href="/docs/frontend">Frontend</a><a class="navbar__item navbar__link" href="/docs/backend">Backend</a><a class="navbar__item navbar__link" href="/docs/devops">DevOps</a><a class="navbar__item navbar__link" href="/docs/projects">Projects</a></div><div class="navbar__items navbar__items--right"><a aria-current="page" class="navbar__item navbar__link navbar__link--active" href="/blog">Blog</a><div class="searchBox_ZlJk"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg width="20" height="20" class="DocSearch-Search-Icon" viewBox="0 0 20 20"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"></span></button></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div id="docusaurus_skipToContent_fallback" class="main-wrapper mainWrapper_z2l0"><div class="container margin-vert--lg"><div class="row"><aside class="col col--3"><nav class="sidebar_re4s thin-scrollbar" aria-label="Blog recent posts navigation"><div class="sidebarItemTitle_pO2u margin-bottom--md">Recent posts</div><ul class="sidebarItemList_Yudw clean-list"><li class="sidebarItem__DBe"><a aria-current="page" class="sidebarItemLink_mo7H sidebarItemLinkActive_I1ZP" href="/blog/go-race-conditions-testing-and-coverage">Go race conditions testing and coverage</a></li><li class="sidebarItem__DBe"><a class="sidebarItemLink_mo7H" href="/blog/accuracy-of-decimal-computations">Accuracy of decimal computations</a></li><li class="sidebarItem__DBe"><a class="sidebarItemLink_mo7H" href="/blog/why-bundle-size-is-important">Why bundle size is important?</a></li><li class="sidebarItem__DBe"><a class="sidebarItemLink_mo7H" href="/blog/prometheus-cardinality-issues">Prometheus Is Out Of Memory. Again.</a></li><li class="sidebarItem__DBe"><a class="sidebarItemLink_mo7H" href="/blog/searching-for-search-engines">The never ending search a search engine 2022-01 edition</a></li></ul></nav></aside><main class="col col--7" itemscope="" itemtype="http://schema.org/Blog"><article itemprop="blogPost" itemscope="" itemtype="http://schema.org/BlogPosting"><header><h1 class="title_f1Hy" itemprop="headline">Go race conditions testing and coverage</h1><div class="container_mt6G margin-vert--md"><time datetime="2023-03-17T00:00:00.000Z" itemprop="datePublished">March 17, 2023</time></div><div class="margin-top--md margin-bottom--sm row"><div class="col col--6 authorCol_Hf19"><div class="avatar margin-bottom--sm"><a href="https://github.com/cvidmar" target="_blank" rel="noopener noreferrer" class="avatar__photo-link"><img class="avatar__photo" src="https://github.com/cvidmar.png" alt="Cristian Vidmar"></a><div class="avatar__intro" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><div class="avatar__name"><a href="https://github.com/cvidmar" target="_blank" rel="noopener noreferrer" itemprop="url"><span itemprop="name">Cristian Vidmar</span></a></div><small class="avatar__subtitle" itemprop="description">Content Chef</small></div></div></div><div class="col col--6 authorCol_Hf19"><div class="avatar margin-bottom--sm"><a href="https://github.com/dreadl0ck" target="_blank" rel="noopener noreferrer" class="avatar__photo-link"><img class="avatar__photo" src="https://github.com/dreadl0ck.png" alt="Philipp Mieden"></a><div class="avatar__intro" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><div class="avatar__name"><a href="https://github.com/dreadl0ck" target="_blank" rel="noopener noreferrer" itemprop="url"><span itemprop="name">Philipp Mieden</span></a></div><small class="avatar__subtitle" itemprop="description">MSc</small></div></div></div></div></header><div id="post-content" class="markdown" itemprop="articleBody"><p>Go has extensive support for concurrency through goroutines and channels. This feature allows programs to progress on several tasks at the same time but it requires some extra care to prevent situations where multiple goroutines might collide and lead to a panic. These are known as race conditions and they happen when a shared variable is read and written at the same time by two different routines. A typical example is a concurrent read/write of a map in memory.</p><p>To illustrate an example let&#x27;s consider <a href="https://github.com/foomo/gocontentful" target="_blank" rel="noopener noreferrer">gocontentful</a>, the code generator that creates an API to access a Contentful CMS from go. The generated API uses an in-memory cache to store a copy of a Contentful space and some extra data structures to allow go programs to access Contentful data, inspect and resolve references and so on. In a typical scenario, the client is used in a service that responds to HTTP calls and makes heavy use of concurrency because it needs to be able, at the same time, to:</p><ul><li>Read entries, assets and references from the cache</li><li>Update/Delete single entities and their connections with others (for example the map of parent entries)</li><li>Incrementally sync the content of the cache with data changes coming from Contentful</li><li>Rebuild the cache entirely and swap the existing one with a new copy</li></ul><p>In addition, for performance reasons, when the cache is created or rebuilt the gocontentful client spawns up to four goroutines to download chunks of data in parallel across the Internet, dynamically selecting the size the sorting of the chunks to leverage the maximum parallelism.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="detecting-race-conditions-through-unit-tests">Detecting race conditions through unit tests<a href="#detecting-race-conditions-through-unit-tests" class="hash-link" aria-label="Direct link to Detecting race conditions through unit tests" title="Direct link to Detecting race conditions through unit tests"></a></h2><p>We experienced race conditions with the client in the past, and we fixed them to maintain it production-ready with every new version. To help with this, we included a testing framework in gocontentful that generates a sample API from a local export of a Contentful space and then runs several unit tests that, among others, test the client for concurrency safety:</p><p><img loading="lazy" alt="make test" src="/assets/images/make-test-e708de4fbdbded8bf679660769b6d1af.webp" width="1570" height="756" class="img_ev3q"></p><p>One of these unit tests spawns tens of thousands of goroutines to concurrently read, write and inspect the references of specific entries while at the same time keeps rebuilding the cache. From the screenshot above we see that no race condition is shown. Even at this concurrency level, though there&#x27;s no guarantee that running</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">go </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">test</span><span class="token plain"> ./</span><span class="token punctuation" style="color:rgb(199, 146, 234)">..</span><span class="token plain">.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>will be enough to generate a collision. What we really want to do is to add a new parameter to enable the go race detector with</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">go </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">test</span><span class="token plain"> -race ./</span><span class="token punctuation" style="color:rgb(199, 146, 234)">..</span><span class="token plain">.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>(In gocontentful you can run <em>make race</em> to fire up both the API generation and the race test)</p><p>From the documentation at <a href="https://go.dev/blog/race-detector" target="_blank" rel="noopener noreferrer">https://go.dev/blog/race-detector</a>:</p><blockquote><p><em>When the -race command-line flag is set, the compiler instruments all memory accesses with code that records when and how the memory was accessed, while the runtime library watches for unsynchronized accesses to shared variables. When such “racy” behavior is detected, a warning is printed.</em></p></blockquote><p>Running this in gocontentful shows that we indeed have a potential collision condition:</p><p><img loading="lazy" alt="Race condition" src="/assets/images/race-condition-5d0a166992c4af58e43708a7eaf41eb9.webp" width="1681" height="1918" class="img_ev3q"></p><p><em>Note: After you run this test you&#x27;ll want to search for &quot;race&quot; inside the terminal output. Make sure you enable a very long (if not infinite) scrollback or you might miss some hits.</em></p><p>The race detector reports the filenames and lines of code that generated the race condition. Looking at those lines in our example shows that a field of the cache (the &quot;offline&quot; boolean) is written protecting it with a proper mutex lock but the lock handling is missing around the read operation:</p><p><img loading="lazy" alt="Read access" src="/assets/images/read-access-e74fbfc1d026ab850775c44f7511bcf0.webp" width="1489" height="402" class="img_ev3q"></p><p><img loading="lazy" alt="Write access" src="/assets/images/write-access-e8b23abfbe4c9a9379f8facf8e1a3c0e.webp" width="1618" height="1004" class="img_ev3q"></p><p>The fix is very simple but in this particular case the offline flag is read and then a 2 seconds delay is started. Deferring the unlock would keep the variable locked for far too long, so we will read-lock it only for the time needed to copy its value to a local variable:</p><p><img loading="lazy" alt="Fix race condition" src="/assets/images/fix-race-condition-e41a80c7dda8ce75c9414b2f2b37e1b8.webp" width="1695" height="538" class="img_ev3q"></p><p>After fixing the issue in the generator templates and regenerating the code, the tests with the race detector run fine. In gocontentful this can be done all in one step with make race:</p><p><img loading="lazy" alt="No race condition" src="/assets/images/no-race-condition-3d4eb2e0a194a1677f60d351fa3798ac.webp" width="1370" height="686" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="test-coverage">Test coverage<a href="#test-coverage" class="hash-link" aria-label="Direct link to Test coverage" title="Direct link to Test coverage"></a></h2><p>That was nice! But how do we know if we&#x27;re covering all test cases? Go has been supporting test code coverage since 1.12 through the -cover option. We can also limit the coverage to a specific package. In our case, we&#x27;re only interested in the <em>testapi</em> sub-package because we want to test the generated API, not the generator itself.</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">go </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">test</span><span class="token plain"> -cover -coverpkg</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain">./test/testapi ./</span><span class="token punctuation" style="color:rgb(199, 146, 234)">..</span><span class="token plain">.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Let&#x27;s try and run the tests with coverage:</p><p><img loading="lazy" alt="Basic coverage" src="/assets/images/basic-coverage-1b4ff1807c3030afadd3d964d0dad9e3.webp" width="1930" height="290" class="img_ev3q"></p><p>The summary shows we are only covering 22% of the code. The goal is not to cover 100%, some parts only work online calling the actual API of a real Contentful space, but we definitely have room for improvement.</p><p>The question is: how do we know exactly which lines of code we&#x27;re covering through the test suite? Again, go test comes to the rescue through another option: <em>-coverprofile</em> lets us specify an output file that will contain references to each single line of code involved in the analysis. It is a text file, but not very readable:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:21.86,22.15 1 1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:25.2,25.18 1 1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:28.2,29.16 2 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:32.2,33.16 2 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:36.2,37.37 2 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:40.2,40.24 1 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:22.15,24.3 1 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:25.18,27.3 1 1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:29.16,31.3 1 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:33.16,35.3 1 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:37.37,39.3 1 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:43.114,44.35 1 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:47.2,48.18 2 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:51.2,53.16 3 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">github.com/foom...tapi/gocontentfulvolibproduct.go:56.2,57.16 2 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">...</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>We can use <em>go tool</em> to convert it to a much better representation in HTML:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">go tool cover -html</span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain">cover.out -o cover.html</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Opening this file in a browser reveals a lot of useful information:</p><p><img loading="lazy" alt="Coverage HTML" src="/assets/images/coverage-html-31939a8031afbabb8f2d0af5c7476644.webp" width="1750" height="1778" class="img_ev3q"></p><p>At the top left of the page there&#x27;s a menu where we can select from all the files analyzed, listed along with the percentage of coverage for each one. Inside every file the lines covered by the tests are green while the red ones are not covered. In the example above we can see that the getAllAssets function is covered but it includes an <em>else</em> condition that is never met.</p><p>In gocontentful (starting from 1.0.18) we can generate the test API, run the tests with coverage, convert the output file and open it in the browser with a single command:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">make</span><span class="token plain"> cover</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>As stated above, not necessarily 100% of the code needs to be covered by the tests, but this view in combination with the race detector gives us incredibly useful information to make the code more solid.</p></div><footer class="row docusaurus-mt-lg blogPostFooterDetailsFull_mRVl"><div class="col"><b>Tags:</b><ul class="tags_jXut padding--none margin-left--sm"><li class="tag_QGVx"><a class="tag_zVej tagRegular_sFm0" href="/blog/tags/golang">golang</a></li><li class="tag_QGVx"><a class="tag_zVej tagRegular_sFm0" href="/blog/tags/concurrency">concurrency</a></li><li class="tag_QGVx"><a class="tag_zVej tagRegular_sFm0" href="/blog/tags/testing">testing</a></li></ul></div><div class="col margin-top--sm"><a href="https://github.com/foomo/foomo-docs/tree/main/foomo/blog/2023-03-17-go-race-conditions-testing-and-coverage/index.mdx" target="_blank" rel="noreferrer noopener" class="theme-edit-this-page"><svg fill="currentColor" height="20" width="20" viewBox="0 0 40 40" class="iconEdit_Z9Sw" aria-hidden="true"><g><path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"></path></g></svg>Edit this page</a></div></footer></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Blog post page navigation"><a class="pagination-nav__link pagination-nav__link--next" href="/blog/accuracy-of-decimal-computations"><div class="pagination-nav__sublabel">Older Post</div><div class="pagination-nav__label">Accuracy of decimal computations</div></a></nav></main><div class="col col--2"><div class="tableOfContents_bqdL thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#detecting-race-conditions-through-unit-tests" class="table-of-contents__link toc-highlight">Detecting race conditions through unit tests</a></li><li><a href="#test-coverage" class="table-of-contents__link toc-highlight">Test coverage</a></li></ul></div></div></div></div></div><footer class="footer"><div class="container container-fluid"><div class="row footer__links"><div class="col footer__col"><div class="footer__title">github</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://github.com/foomo" target="_blank" rel="noopener noreferrer" class="footer__link-item">https://github.com/foomo</a></li></ul></div><div class="col footer__col"><div class="footer__title">legal</div><ul class="footer__items clean-list"><li class="footer__item"><a class="footer__link-item" href="/etc/imprint">Imprint</a></li></ul></div></div><div class="footer__bottom text--center"><div class="footer__copyright">© 2023 bestbytes</div></div></div></footer></div>
<script src="/assets/js/runtime~main.24c19dd7.js"></script>
<script src="/assets/js/main.4cf1833b.js"></script>
</body>
</html>