<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Julia Community 🟣: Daniel Pinyol</title>
    <description>The latest articles on Julia Community 🟣 by Daniel Pinyol (@dpinol).</description>
    <link>https://forem.julialang.org/dpinol</link>
    <image>
      <url>https://forem.julialang.org/images/KnWZH4hT5GSUCCTiaiB2TksOvAA5zeoyJo9x2GFGP54/rs:fill:90:90/g:sm/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L3VzZXIvcHJvZmls/ZV9pbWFnZS82MDAv/YmJhY2IyNTMtYzZl/MS00OWE5LWJiMTYt/YjhkYTNlY2QzYjIw/LmpwZWc</url>
      <title>Julia Community 🟣: Daniel Pinyol</title>
      <link>https://forem.julialang.org/dpinol</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.julialang.org/feed/dpinol"/>
    <language>en</language>
    <item>
      <title>Test Data Builder in Julia</title>
      <dc:creator>Daniel Pinyol</dc:creator>
      <pubDate>Mon, 27 Feb 2023 23:17:45 +0000</pubDate>
      <link>https://forem.julialang.org/dpinol/test-data-builder-in-julia-j7o</link>
      <guid>https://forem.julialang.org/dpinol/test-data-builder-in-julia-j7o</guid>
      <description>&lt;h4&gt;
  
  
  Short tests are better
&lt;/h4&gt;

&lt;p&gt;Ideally, we'd like tests to have 3 lines of code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Setup&lt;/strong&gt;: aka &lt;em&gt;Given&lt;/em&gt; or &lt;em&gt;Arrange&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run&lt;/strong&gt;:   aka &lt;em&gt;When&lt;/em&gt;  or &lt;em&gt;Act&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test&lt;/strong&gt;:  aka &lt;em&gt;Then&lt;/em&gt;  or &lt;em&gt;Assert&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But they often require setting up a lot of data before actually executing the function to test. This causes several drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The tests get longer, less clear and more error prone.&lt;/li&gt;
&lt;li&gt;Developers may feel lazy to create new tests if the required effort is too high.&lt;/li&gt;
&lt;li&gt;The explosion in the fan-in of the setup code will slow down potential refactors.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  How to reduce the setup phase
&lt;/h4&gt;

&lt;p&gt;In this post I'll present some examples on using the &lt;a href="http://wiki.c2.com/?TestDataBuilder" rel="noopener noreferrer"&gt;Test Data Builder&lt;/a&gt; pattern to mitigate this issue.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Model
&lt;/h4&gt;

&lt;p&gt;Our toy model represents Circles and Squares which are fully enclosed in a Canvas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@kwdef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Canvas&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
  &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@kwdef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Position&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
  &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@kwdef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Circle&lt;/span&gt;
    &lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt;
    &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@kwdef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Square&lt;/span&gt;
    &lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt;
    &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Builder pattern
&lt;/h3&gt;

&lt;p&gt;Using Julia's &lt;code&gt;Base.@kwdef&lt;/code&gt;, you can quickly create a Builder for &lt;code&gt;Position&lt;/code&gt; values. &lt;br&gt;
The builder should ensure that the non-explicitly set fields will get consistent values. In this simple case, you could actually enforce inconsistent instances to define unhappy path tests. However, when available, I prefer Builders to transparently invoke the model integrity tests to ensure that test setups start from consistent data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;TEST_CANVAS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;480&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;randX&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;randY&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@kwdef&lt;/span&gt; &lt;span class="k"&gt;mutable struct&lt;/span&gt;&lt;span class="nc"&gt; PositionBuilder&lt;/span&gt; 
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TEST_CANVAS&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;randX&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;randY&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PositionBuilder&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Position&lt;/span&gt;&lt;span class="x"&gt;(;&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since all data takes default values, the intent of the setup code becomes evident&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# testing a position at first column&lt;/span&gt;
&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PositionBuilder&lt;/span&gt;&lt;span class="x"&gt;(;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;# testing a position at a 1-pixel-sized Canvas&lt;/span&gt;
&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PositionBuilder&lt;/span&gt;&lt;span class="x"&gt;(;&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Builders are composable
&lt;/h3&gt;

&lt;p&gt;You can define your &lt;code&gt;CircleBuilder&lt;/code&gt; &amp;amp; &lt;code&gt;SquareBuilder&lt;/code&gt; to compose PositionBuilder to create the shape center.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@kwdef&lt;/span&gt; &lt;span class="k"&gt;mutable struct&lt;/span&gt;&lt;span class="nc"&gt; CircleBuilder&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TEST_CANVAS&lt;/span&gt;
    &lt;span class="n"&gt;centerB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PositionBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PositionBuilder&lt;/span&gt;&lt;span class="x"&gt;(;&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;randRadius&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;centerB&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; randRadius&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PositionBuilder&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;maxRadius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;maxRadius&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; Base.rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CircleBuilder&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;randRadius&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;centerB&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;(;&lt;/span&gt; &lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;centerB&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Going farther
&lt;/h4&gt;

&lt;p&gt;However, we suffer several limitations in the current design:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If we do &lt;code&gt;CircleBuilder().radius = X&lt;/code&gt;, the assigned value will get lost when creating a Circle.&lt;/li&gt;
&lt;li&gt;We need to invoke &lt;code&gt;radius = randRadius(...)&lt;/code&gt; twice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To avoid these shortcomings, I tend to use the following design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@kwdef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; SquareBuilder&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PositionBuilder&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;canvas!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SquareBuilder&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@something&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt; &lt;span class="n"&gt;TEST_CANVAS&lt;/span&gt;
&lt;span class="n"&gt;center!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SquareBuilder&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;centerB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@something&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;centerB&lt;/span&gt; &lt;span class="n"&gt;PositionBuilder&lt;/span&gt;&lt;span class="x"&gt;(;&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;canvas!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;side!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SquareBuilder&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@something&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="n"&gt;calculateSide&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canvas!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="n"&gt;center!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; Base.rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SquareBuilder&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;center&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;center!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;side!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Square&lt;/span&gt;&lt;span class="x"&gt;(;&lt;/span&gt; &lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, all fields are &lt;code&gt;nothing&lt;/code&gt; until the tests assign a specific value, or they are automatically assigned during the construction of the struct. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Regression tests gotchas in Julia</title>
      <dc:creator>Daniel Pinyol</dc:creator>
      <pubDate>Sun, 14 Aug 2022 09:59:00 +0000</pubDate>
      <link>https://forem.julialang.org/dpinol/regression-tests-gotchas-2052</link>
      <guid>https://forem.julialang.org/dpinol/regression-tests-gotchas-2052</guid>
      <description>&lt;p&gt;Imagine that you have a function which computes a result from a large vector of numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myfun&lt;/span&gt;&lt;span class="x"&gt;([&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your battery of tests, you may want to ensure that the performance of your function does not degrade due to future updates of your code. Since we developers are lazy, we may be tempted to try to always run the test with the same fixed input and then check the time and memory performance.  &lt;/p&gt;

&lt;p&gt;You may end up with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;Test&lt;/span&gt;

&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;inputlen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputlen&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myfun&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EXPECTED&lt;/span&gt;

&lt;span class="n"&gt;perf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@timed&lt;/span&gt; &lt;span class="n"&gt;myfun&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;perf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;TIME_THRESHOLD&lt;/span&gt;
&lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;perf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;BYTES_THRESHOLD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since you're clever, you fixed the random seed. So, what can go wrong?&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Random engine does not generate the same values accross Julia versions
&lt;/h3&gt;

&lt;p&gt;This happened from 1.6 to 1.7. It's possible that it won't happen again in a long time, but if you have many of these tests it may be a pain to change the expected results. Tp ensure stable results, you should use &lt;a href="https://github.com/JuliaRandom/StableRNGs.jl"&gt;StableRNGs&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Relying on the global Random engine is error prone
&lt;/h3&gt;

&lt;p&gt;Once I was bitten by this. At one point, I realized that &lt;em&gt;sometimes&lt;/em&gt; my tested function was not behaving as expected.&lt;/p&gt;

&lt;p&gt;After some digging, I found that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The random seed was set at the beginning of the file.&lt;/li&gt;
&lt;li&gt;Then I was creating a &lt;code&gt;struct S&lt;/code&gt; in order to call &lt;code&gt;myfun(s)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I realized that &lt;code&gt;S&lt;/code&gt;'s constructor was doing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;    &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; myfinalizer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;
                &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bt&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;current_exceptions&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
                    &lt;span class="n"&gt;showerror&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;stderr&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bt&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; S&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;....&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;finalizer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myfinalizer&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What is this doing?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It registers a &lt;code&gt;finalizer&lt;/code&gt; so that the &lt;code&gt;S&lt;/code&gt; object automatically closes its acquired resources when it's destroyed.&lt;/li&gt;
&lt;li&gt;Since finalizers are called from GC thread, you cannot directly call IO stuff. So you need &lt;code&gt;@async&lt;/code&gt; to close your resources from a different thead.&lt;/li&gt;
&lt;li&gt;Last but not least, exceptions must be caught and logged since Julia will silently swallow them in this case.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  So what?
&lt;/h3&gt;

&lt;p&gt;What happened was that &lt;code&gt;@async&lt;/code&gt; internally uses &lt;code&gt;Random&lt;/code&gt; (maybe to generate the &lt;code&gt;Task&lt;/code&gt; id), which screwed the reproducibility of the random function input.&lt;/p&gt;

&lt;h3&gt;
  
  
  But why was did the input only changed &lt;em&gt;sometimes&lt;/em&gt;?
&lt;/h3&gt;

&lt;p&gt;I guess that, as the execution of GC is not controlled by my code, depending on how the &lt;code&gt;finalizer&lt;/code&gt; is called, the input may be either randomly generated with the fresh seed, or after &lt;code&gt;@async&lt;/code&gt;'s usage of &lt;code&gt;Random&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;finalizer&lt;/code&gt;'s are dodgy in all GC languages. If possible use &lt;a href="https://www.juliabloggers.com/julia-do-block-vs-python-with-statement/"&gt;do blocks&lt;/a&gt; instead. &lt;/li&gt;
&lt;li&gt;In general, your code should be very explicit on its dependencies (&lt;code&gt;Random&lt;/code&gt; here). They should be &lt;a href="https://en.wikipedia.org/wiki/Dependency_injection"&gt;injected&lt;/a&gt; to avoid side effects.&lt;/li&gt;
&lt;li&gt;Don't be (so) lazy, and avoid randomly generated inputs for regression tests. You can easily seralize input data with Julia &lt;a href="https://docs.julialang.org/en/v1/stdlib/Serialization/"&gt;serialization&lt;/a&gt;, or with libraries such as &lt;a href="https://github.com/JuliaIO/JLD2.jl"&gt;JLD2&lt;/a&gt;. JLD2 is supposed to be more stable accross Julia versions, but in any case it's wise to serialize simple data types as &lt;code&gt;Dict&lt;/code&gt;'s or &lt;code&gt;Vector&lt;/code&gt; only.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Update 2022/8/30
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Thank you &lt;a class="mentioned-user" href="https://forem.julialang.org/chrisrackauckas"&gt;@chrisrackauckas&lt;/a&gt; for point me out to &lt;a href="https://github.com/JuliaRandom/StableRNGs.jl"&gt;StableRNGs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Asserting on wall clock duration of tests is very fragile. There are plenty of tools which measure instead the number of executed instruction. Eg &lt;a href="https://github.com/triscale-innov/GFlops.jl"&gt;GFlops.jl&lt;/a&gt; counts the number of floating point operations, &lt;a href="https://github.com/JuliaPerf/LinuxPerf.jl"&gt;LinuxPerf.jl&lt;/a&gt; counts linux events, or &lt;a href="https://github.com/JuliaPerf/LIKWID.jl"&gt;LIKWID.jl&lt;/a&gt; which wraps linux likwid tool.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>resources</category>
    </item>
    <item>
      <title>Detecting @test @allocated gotchas</title>
      <dc:creator>Daniel Pinyol</dc:creator>
      <pubDate>Wed, 03 Aug 2022 08:34:18 +0000</pubDate>
      <link>https://forem.julialang.org/dpinol/detecting-test-allocated-gotchas-34op</link>
      <guid>https://forem.julialang.org/dpinol/detecting-test-allocated-gotchas-34op</guid>
      <description>&lt;p&gt;Hi,&lt;br&gt;
if you care about performance, you probably end up having a lot of these on your unit tests, to ensure that your functions do not allocate when they don't need to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="nd"&gt;@allocated&lt;/span&gt; &lt;span class="n"&gt;testedfunction&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far so good. But, if you need assignments or comparisons, you'll need some brackets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@allocated&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;yourexpression&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where you start thinking on DRY: should you create a macro to enable more expressive tests?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="nd"&gt;@testnoallocations&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;yourexpression&lt;/span&gt;

&lt;span class="k"&gt;macro&lt;/span&gt;&lt;span class="nf"&gt; testnoallocations&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expressions&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;esc&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;quote&lt;/span&gt;
            &lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;iszero&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@allocated&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expressions&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I confess I'm a encapsulation junkie, so I'll try to justify why I think it's a good idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Global non-consts
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"foo"&lt;/span&gt;
&lt;span class="nd"&gt;@testset&lt;/span&gt; &lt;span class="s"&gt;"myset"&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="nd"&gt;@allocated&lt;/span&gt; &lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your tests are complex or you're a Julia noob, it's not difficult to miss that this test will fail because &lt;code&gt;foo&lt;/code&gt; is a non-const global. Why not adding the check within your macro? You just need to recursively get all the symbols, and find those which are global (&lt;code&gt;isdefined(__module__, arg)&lt;/code&gt;) but &lt;code&gt;!isconst&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;
&lt;span class="k"&gt;macro&lt;/span&gt;&lt;span class="nf"&gt; testnoallocations&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expressions&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_failIfNonConstGlobalsInExpressions&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__module__&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expressions&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;esc&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;....&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; _failIfNonConstGlobalsInExpressions&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Module&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expressions&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;expressions&lt;/span&gt;
        &lt;span class="n"&gt;nonConstGlobals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;expressionsymbols&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isdefined&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isconst&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isempty&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nonConstGlobals&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"testnoallocations called with expression containing non const global symbols &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(collect(
            nonConstGlobals
        ))"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
            &lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="s"&gt;" Return all the symbols that make up an expression (or itself if a symbol is passed)"&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; expressionsymbols&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Union&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Expr&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Symbol&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Number&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;isa&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Expr&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;,)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;isa&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Symbol&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;topSymbols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;isa&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Symbol&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;subExpressions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expressionsymbols&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;isa&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Expr&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topSymbols&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Iterators&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subExpressions&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. &lt;code&gt;@testset&lt;/code&gt;s are not functions
&lt;/h2&gt;

&lt;p&gt;I don't have a MWE for this, but a more difficult gotcha is when your function allocates because you're calling it from a function. Indeed, &lt;code&gt;@testset&lt;/code&gt; bodies run from the global scope. You can detect this by exploiting the fact that requesting the current function with &lt;code&gt;nameof(var"#self#")&lt;/code&gt; will fail when called from the global scope.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="k"&gt;macro&lt;/span&gt;&lt;span class="nf"&gt; testnoallocations&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expressions&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;esc&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;quote&lt;/span&gt;
            &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nd"&gt;@isCalledFromFunction&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                &lt;span class="nd"&gt;@warn&lt;/span&gt; &lt;span class="s"&gt;"Since not called from a function @allocated could be imprecise"&lt;/span&gt;
            &lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@allocated&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expressions&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
    &lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;macro&lt;/span&gt;&lt;span class="nf"&gt; isCalledFromFunction&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;esc&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
            &lt;span class="n"&gt;currentFunctionName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nameof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="s"&gt;"#self#"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt;
            &lt;span class="nb"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Running with --compile=min
&lt;/h2&gt;

&lt;p&gt;A trick to run short tests very quickly is running them with &lt;code&gt;--compile=min&lt;/code&gt;. Unfortunately, the interpreter may then allocate extra memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="k"&gt;macro&lt;/span&gt;&lt;span class="nf"&gt; testnoallocations&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expressions&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;iscompileenabled&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
        &lt;span class="nd"&gt;@warn&lt;/span&gt; &lt;span class="s"&gt;"Allocations measures are not precise because executed with --compile=min"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;iscompileenabled&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JLOptions&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile_enabled&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  That's all
&lt;/h2&gt;

&lt;p&gt;I hope the post is also useful to show how powerful are Julia reflection capabilities. You comments are more than welcome, specially if you know similar tricks to ease your testing experience. Happy testing!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>macros</category>
      <category>allocations</category>
    </item>
  </channel>
</rss>
