Skip to content

Commit be3c011

Browse files
deploy: 2e0aa3e
1 parent f0904f1 commit be3c011

File tree

5 files changed

+227
-2
lines changed

5 files changed

+227
-2
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<!doctype html>
2+
<html lang="en"><head><meta charset="UTF-8"><title>Test timeouts in Gleam | Gears</title><link href="/style.css" rel="stylesheet"><link href="https://rsms.me/" rel="preconnect"><link href="https://rsms.me/inter/inter.css" rel="stylesheet"><link href="/favicon.svg" rel="icon" type="image/svg+xml"><link href="/favicon.ico" rel="alternate icon" sizes="48x48" type="image/png"><link href="/apple-touch-icon.png" rel="apple-touch-icon" sizes="192x192"></head><body class="min-h-screen bg-slate-800 text-white"><header class="bg-purple-700 flex w-full select-none fixed"><nav class="flex h-16 grow items-center justify-between gap-4 px-4 text-white"><a class="heading" href="/"><figure class="flex flex-shrink-0 items-center space-x-1"><img alt="Gears logo" class="w-16" height="64" src="/images/logo.svg" style="image-rendering:optimizeSpeed;image-rendering:pixelated;" width="64"><figcaption class="text-4xl font-bold font-celandine hidden md:block"><svg xmlns="http://www.w3.org/2000/svg"><path xmlns="http://www.w3.org/2000/svg" class="animate-write" d="m 46.655648,87.273021 c 0.08105,4.602277 0.692125,6.832404 4.69733,4.361206 l 1.696146,2.728148 -14.225762,9.638735 c -2.121146,-1.55607 -3.838276,-4.0442 -4.678364,-7.342384 -3.390161,3.734254 -7.562386,6.271464 -13.082724,5.788494 C 10.581627,101.53029 2.8720353,90.133692 1.4418114,77.915968 0.26959238,64.592176 8.4519982,56.762609 16.647423,50.627146 c 6.709353,-4.814367 12.185664,-6.592535 16.425926,-6.221561 10.400636,0.909937 10.2848,16.055858 17.661218,12.831577 l 1.369105,2.78015 -13.921761,7.085583 C 29.896397,65.974923 28.524127,49.408937 19.629655,55.241389 c -4.548238,3.149245 -6.106269,8.978614 -4.24704,19.057204 1.461159,6.335369 5.770507,14.209799 12.214927,17.030887 2.403158,1.097035 4.205262,0.770993 6.046357,-0.922122 l 0.08399,-0.960053 0.300983,-3.440212 c -4.7513,-0.980005 -7.300468,-2.251058 -10.07761,0.0051 L 22.053124,83.749873 33.3637,74.259168 c 9.7606,0.853945 14.332923,4.801125 19.271184,0.879838 l 1.905129,2.18211 -7.907394,7.450543 c -0.0043,0.967047 0.07203,1.94111 0.02303,2.501151 z m 27.106123,0.967939 c 3.255101,3.879267 7.924879,5.594901 14.610274,2.013462 L 89.84759,92.997687 71.511182,104.62772 C 62.371276,103.58301 53.002118,82.748534 59.5709,73.029912 61.983775,69.728218 72.927415,59.493724 79.332135,60.054064 85.49365,60.593125 92.577447,70.199104 92.109323,75.549881 Z M 70.902742,68.629587 c -3.536763,2.141364 -1.750046,9.73174 1.253752,15.549655 L 79.558343,78.94493 C 78.612783,73.878931 74.248977,66.798328 70.902742,68.629587 Z m 62.806708,23.029124 1.74528,2.221251 -13.08952,10.709588 c -2.6179,0 -5.2358,-4.839141 -5.71178,-8.567661 l -11.7409,8.567661 c -6.505075,-0.0794 -10.709601,-7.61571 -9.916305,-13.24816 1.269292,-8.250355 9.123005,-13.010178 19.832605,-16.104062 -3.49054,-5.553124 -13.08951,-4.997811 -19.0393,-1.348612 l -1.586601,-2.459244 14.993441,-10.78893 c 9.28166,0 19.19795,1.665941 19.43593,14.438129 l 0.0794,12.375534 c -0.0794,4.125169 1.26928,6.901741 4.99782,4.204506 z m -22.60914,3.490534 4.99781,-3.252544 -0.0794,-12.930849 c -10.23362,3.728531 -10.70961,12.930849 -4.91847,16.183393 z m 51.19467,-16.075266 c -2.21157,-3.712255 -5.13397,-8.846224 -8.45131,-5.765843 l -0.47391,0.473911 v 1.184756 l 0.079,12.795432 c -0.079,4.028194 1.18475,6.555693 4.897,3.949214 l 1.81664,2.448509 -13.0324,10.504892 c -3.55428,-2.21157 -6.23973,-6.792636 -6.16074,-13.664255 V 78.679058 c -0.15797,-4.818028 -1.97461,-6.397712 -6.63468,-3.396316 L 132.99186,72.755248 147.13,60.907625 c 3.08039,2.290541 4.89704,4.423114 5.68687,7.740447 l 8.05639,-7.740447 c 6.00279,0 11.68966,9.636068 11.68966,9.636068 z m 19.14678,-9.156439 c 4.4536,7.601839 23.80372,5.375035 23.80372,20.962647 0,10.442913 -10.13577,13.207223 -19.5037,13.207223 -7.14112,0 -13.8983,-2.45716 -16.12512,-6.757188 l 11.36438,-10.596498 1.84287,1.45894 c -2.91787,2.841084 11.134,6.987545 15.05009,6.987545 0.46073,-6.296472 -26.33768,-10.058994 -26.33768,-19.580489 0,-8.369699 13.89831,-14.05188 13.89831,-14.05188 7.83221,0 16.2019,4.4536 21.11622,1.61251 l 1.22858,1.689298 -12.90008,10.289358 c -4.99112,0 -11.44115,-8.292917 -13.43759,-5.221466 z" style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-dasharray:330;stroke-dashoffset:330;"></path></svg></figcaption></figure></a></nav></header><main class="py-24"><div class="mx-auto max-w-3xl"><h1 class="text-3xl font-bold leading-tight text-center">Test timeouts in Gleam</h1></div><div class="mx-auto max-w-4xl py-8 leading-8"><h2 class="text-xl font-bold text-center m-0">2025-11-25</h2><p>If you don&#39;t care about the backstory and just want to know the answer, you can
3+
<a class="underline" href="#How-do-I-configure-my-test-timeout" target>skip there</a>.</p><h2 id="How-Gleam-tests-work">How Gleam tests work</h2><p>When you create a new Gleam project, there are two dependencies that are added
4+
automatically by the build tool. These are <a class="underline" href="https://hexdocs.pm/gleam_stdlib" target="_blank"><code>gleam_stdlib</code></a>,
5+
the Gleam standard library, and <a class="underline" href="https://hexdocs.pm/gleeunit" target="_blank"><code>gleeunit</code></a>, the
6+
default test runner for Gleam.</p><p>Both of these dependencies are technically optional, but they are added by default
7+
to make it easier to start a new project, since most projects use both the above
8+
packages.</p><p>See, when you run <code>gleam test</code> in the terminal, while it may seem that the Gleam
9+
build tool finds all your test functions magically, all it&#39;s really doing is calling
10+
the <code>main</code> function of the module named <code>$PROJECT_test</code>, which by default calls
11+
out to <code>gleeunit</code>, which is the code that&#39;s really in charge of running your tests.</p><h2 id="What-is-gleeunit">What is gleeunit?</h2><p>On the Erlang target, the <code>gleeunit</code> library is a thin wrapper for
12+
<a class="underline" href="https://www.erlang.org/doc/apps/eunit/chapter.html" target="_blank"><code>EUnit</code></a>, which is a test
13+
runner built into OTP. On JavaScript, <code>gleeunit</code> implements a custom test runner
14+
that has similar (but not identical) features to <code>EUnit</code>.</p><p>Somewhat unfortunately, EUnit comes with a default timeout of 5 seconds, and
15+
seemingly <a class="underline" href="https://github.com/lpil/gleeunit/issues/51" target="_blank">no way to globally adjust it</a>
16+
so if any test runs for longer than that it is immediately terminated.</p><p>EUnit has a somewhat unconventional API. As the default Gleam project explains,
17+
it runs all public functions whose names end in <code>_test</code>. But there&#39;s a second
18+
more obscure API, in the form of <strong>test generators</strong>.</p><p>Test generators are another kind of EUnit test. Test generator functions end
19+
in <code>*_test_</code> (note the trailing underscore), and they have the ability to configure
20+
various parts of how EUnit runs. It&#39;s detailed in the EUnit documentation,
21+
but here we will just cover the most commonly used one, which is the timeout.</p><h2 id="How-do-I-configure-my-test-timeout">How do I configure my test timeout?</h2><p>EUnit test generators allow returning extra values along with a function to call
22+
in order to configure it. In Erlang syntax, here is how you would configure the
23+
timeout for your test, to be 60 seconds instead of 5:</p><pre><code data-lang="erlang"><span><span class=hl-function>something_test_</span><span class=hl-punctuation>(</span><span class=hl-punctuation>)</span> <span class=hl-punctuation>-&gt;</span>
24+
<span class=hl-punctuation>{</span><span class=hl-atom>timeout</span><span class=hl-punctuation>,</span> <span class=hl-number>60</span><span class=hl-punctuation>,</span> <span class=hl-keyword>fun</span> <span class=hl-punctuation>(</span><span class=hl-punctuation>)</span> <span class=hl-punctuation>-&gt;</span>
25+
<span class=hl-punctuation>...</span> <span class=hl-comment>% The actual body of your test
26+
</span> <span class=hl-punctuation>}</span><span class=hl-punctuation>.</span>
27+
</span></code></pre><p>So, what does this translate to Gleam? Well, there are actually two ways to do it.
28+
The first is to use the <code>atom.create</code> function from the <a class="underline" href="https://hexdocs.pm/gleam_erlang" target="_blank"><code>gleam_erlang</code></a>
29+
library. This creates basically the same as the Erlang code:</p><pre><code data-lang="gleam"><span><span class=hl-keyword>import</span> <span class=hl-module>gleam/erlang/atom</span>
30+
31+
<span class=hl-keyword>pub</span> <span class=hl-keyword>fn</span> <span class=hl-function>something_test_</span>() {
32+
#(<span class=hl-module>atom</span>.<span class=hl-function>create</span>(<span class=hl-string>&quot;timeout&quot;</span>), <span class=hl-number>60</span>, <span class=hl-keyword>fn</span>() {
33+
... <span class=hl-comment>// The actual body of your test</span>
34+
})
35+
}
36+
</span></code></pre><p>This works, but it has a couple of drawbacks. Firstly, you need to pull in a new
37+
library just to create this one atom, and secondly it just doesn&#39;t look very nice.
38+
My preferred solution takes advantage of the way that custom types are represented
39+
in Gleam.</p><p>When Gleam compiles to Erlang, Gleam custom types become an Erlang tuple tagged
40+
with an atom. So the Gleam value <code>Ok(10)</code> becomes the Erlang <code>{ok, 10}</code>. This is
41+
perfect for what we need, because it&#39;s the exact same format as what EUnit expects.
42+
Using that knowledge we can define a custom type to represent the timeout tuple:</p><pre><code data-lang="gleam"><span><span class=hl-keyword>pub</span> <span class=hl-keyword>type</span> <span class=hl-variant>Timeout</span>(a) {
43+
<span class=hl-variant>Timeout</span>(time: <span class=hl-variant>Int</span>, function: <span class=hl-keyword>fn</span>() -&gt; a)
44+
}
45+
</span></code></pre><p>You can use <code>Float</code> instead for <code>time</code> here, but I find I rarely need to set my
46+
timeout to a fraction of a second, so <code>Int</code> is more convenient. Now, we can take
47+
advantage of Gleam&#39;s <a class="underline" href="https://tour.gleam.run/advanced-features/use/" target="_blank"><code>use</code> syntax</a>,
48+
which allows us to turn a call to a higher order function — where we pass an anonymous
49+
function, like we did in the previous example — into a flattened statement.
50+
We now get a much nicer looking test:</p><pre><code data-lang="gleam"><span><span class=hl-keyword>pub</span> <span class=hl-keyword>type</span> <span class=hl-variant>Timeout</span>(a) {
51+
<span class=hl-variant>Timeout</span>(time: <span class=hl-variant>Int</span>, function: <span class=hl-keyword>fn</span>() -&gt; a)
52+
}
53+
54+
<span class=hl-keyword>pub</span> <span class=hl-keyword>fn</span> <span class=hl-function>something_test_</span>() {
55+
<span class=hl-keyword>use</span> &lt;- <span class=hl-variant>Timeout</span>(<span class=hl-number>60</span>)
56+
... <span class=hl-comment>// The actual body of your test</span>
57+
}
58+
</span></code></pre><p>Great! We&#39;ve successfully worked around EUnit&#39;s annoying timeout. Job done! But
59+
wait...</p><h2 id="Multi-target-tests">Multi-target tests</h2><p>Remember when I mentioned earlier about how <code>gleeunit</code> works differently on the
60+
JavaScript target? Well, the good news is that it doesn&#39;t have a timeout like on
61+
the Erlang target, but the bad news is that it doesn&#39;t support test generators.
62+
So if all your tests are called <code>*_test_</code>, none of them are going to run on
63+
the JavaScript target.</p><p>The only real way to get around this is to write a bit of extra boilerplate for
64+
all of your tests. You need to write a test that runs only on JavaScript, that
65+
calls the same code as the Erlang test. There&#39;s only really one way achieve this,
66+
with the <code>@target</code> attribute, and it&#39;s not pretty. Here&#39;s an example:</p><pre><code data-lang="gleam"><span><span class=hl-comment>// This runs on Erlang</span>
67+
<span class=hl-keyword>pub</span> <span class=hl-keyword>fn</span> <span class=hl-function>something_test_</span>() {
68+
<span class=hl-keyword>use</span> &lt;- <span class=hl-variant>Timeout</span>(<span class=hl-number>60</span>)
69+
... <span class=hl-comment>// The actual body of your test</span>
70+
}
71+
72+
<span class=hl-comment>// This runs on JavaScript</span>
73+
<span class=hl-keyword>@target</span>(javascript)
74+
<span class=hl-keyword>pub</span> <span class=hl-keyword>fn</span> <span class=hl-function>something_test</span>() {
75+
<span class=hl-function>something_test_</span>().<span class=hl-function>function</span>()
76+
}
77+
</span></code></pre><p>The <code>@target</code> attribute is deprecated, and you shouldn&#39;t use it really, but there
78+
isn&#39;t any alternative for this specific case. Hopefully soon we will have an
79+
alternative, both to <code>@target</code>, and to this problem of different behaviour in the
80+
test runner across targets.</p><h2 id="Conclusion">Conclusion</h2><p>If you want to change the timeout of a test that only runs on Erlang, it&#39;s not
81+
very hard. If you want to have tests that run on both targets for longer than 5
82+
seconds, it gets messy.
83+
Hopefully there will be a better way to do it in the near future.</p></div></main><footer class="right-5 bottom-4 fixed">Made with <a class="underline" href="https://lustre.build" target="_blank">Lustre</a> and <a class="underline" href="https://gleam.run" target="_blank">Gleam</a><br>Source: <a class="underline" href="https://github.com/GearsDatapacks/GearsDatapacks.github.io" target="_blank">GitHub</a></footer></body></html>

blog/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<!doctype html>
2+
<html lang="en"><head><meta charset="UTF-8"><title>Blog | Gears</title><link href="/style.css" rel="stylesheet"><link href="https://rsms.me/" rel="preconnect"><link href="https://rsms.me/inter/inter.css" rel="stylesheet"><link href="/favicon.svg" rel="icon" type="image/svg+xml"><link href="/favicon.ico" rel="alternate icon" sizes="48x48" type="image/png"><link href="/apple-touch-icon.png" rel="apple-touch-icon" sizes="192x192"></head><body class="min-h-screen bg-slate-800 text-white"><header class="bg-purple-700 flex w-full select-none fixed"><nav class="flex h-16 grow items-center justify-between gap-4 px-4 text-white"><a class="heading" href="/"><figure class="flex flex-shrink-0 items-center space-x-1"><img alt="Gears logo" class="w-16" height="64" src="/images/logo.svg" style="image-rendering:optimizeSpeed;image-rendering:pixelated;" width="64"><figcaption class="text-4xl font-bold font-celandine hidden md:block"><svg xmlns="http://www.w3.org/2000/svg"><path xmlns="http://www.w3.org/2000/svg" class="animate-write" d="m 46.655648,87.273021 c 0.08105,4.602277 0.692125,6.832404 4.69733,4.361206 l 1.696146,2.728148 -14.225762,9.638735 c -2.121146,-1.55607 -3.838276,-4.0442 -4.678364,-7.342384 -3.390161,3.734254 -7.562386,6.271464 -13.082724,5.788494 C 10.581627,101.53029 2.8720353,90.133692 1.4418114,77.915968 0.26959238,64.592176 8.4519982,56.762609 16.647423,50.627146 c 6.709353,-4.814367 12.185664,-6.592535 16.425926,-6.221561 10.400636,0.909937 10.2848,16.055858 17.661218,12.831577 l 1.369105,2.78015 -13.921761,7.085583 C 29.896397,65.974923 28.524127,49.408937 19.629655,55.241389 c -4.548238,3.149245 -6.106269,8.978614 -4.24704,19.057204 1.461159,6.335369 5.770507,14.209799 12.214927,17.030887 2.403158,1.097035 4.205262,0.770993 6.046357,-0.922122 l 0.08399,-0.960053 0.300983,-3.440212 c -4.7513,-0.980005 -7.300468,-2.251058 -10.07761,0.0051 L 22.053124,83.749873 33.3637,74.259168 c 9.7606,0.853945 14.332923,4.801125 19.271184,0.879838 l 1.905129,2.18211 -7.907394,7.450543 c -0.0043,0.967047 0.07203,1.94111 0.02303,2.501151 z m 27.106123,0.967939 c 3.255101,3.879267 7.924879,5.594901 14.610274,2.013462 L 89.84759,92.997687 71.511182,104.62772 C 62.371276,103.58301 53.002118,82.748534 59.5709,73.029912 61.983775,69.728218 72.927415,59.493724 79.332135,60.054064 85.49365,60.593125 92.577447,70.199104 92.109323,75.549881 Z M 70.902742,68.629587 c -3.536763,2.141364 -1.750046,9.73174 1.253752,15.549655 L 79.558343,78.94493 C 78.612783,73.878931 74.248977,66.798328 70.902742,68.629587 Z m 62.806708,23.029124 1.74528,2.221251 -13.08952,10.709588 c -2.6179,0 -5.2358,-4.839141 -5.71178,-8.567661 l -11.7409,8.567661 c -6.505075,-0.0794 -10.709601,-7.61571 -9.916305,-13.24816 1.269292,-8.250355 9.123005,-13.010178 19.832605,-16.104062 -3.49054,-5.553124 -13.08951,-4.997811 -19.0393,-1.348612 l -1.586601,-2.459244 14.993441,-10.78893 c 9.28166,0 19.19795,1.665941 19.43593,14.438129 l 0.0794,12.375534 c -0.0794,4.125169 1.26928,6.901741 4.99782,4.204506 z m -22.60914,3.490534 4.99781,-3.252544 -0.0794,-12.930849 c -10.23362,3.728531 -10.70961,12.930849 -4.91847,16.183393 z m 51.19467,-16.075266 c -2.21157,-3.712255 -5.13397,-8.846224 -8.45131,-5.765843 l -0.47391,0.473911 v 1.184756 l 0.079,12.795432 c -0.079,4.028194 1.18475,6.555693 4.897,3.949214 l 1.81664,2.448509 -13.0324,10.504892 c -3.55428,-2.21157 -6.23973,-6.792636 -6.16074,-13.664255 V 78.679058 c -0.15797,-4.818028 -1.97461,-6.397712 -6.63468,-3.396316 L 132.99186,72.755248 147.13,60.907625 c 3.08039,2.290541 4.89704,4.423114 5.68687,7.740447 l 8.05639,-7.740447 c 6.00279,0 11.68966,9.636068 11.68966,9.636068 z m 19.14678,-9.156439 c 4.4536,7.601839 23.80372,5.375035 23.80372,20.962647 0,10.442913 -10.13577,13.207223 -19.5037,13.207223 -7.14112,0 -13.8983,-2.45716 -16.12512,-6.757188 l 11.36438,-10.596498 1.84287,1.45894 c -2.91787,2.841084 11.134,6.987545 15.05009,6.987545 0.46073,-6.296472 -26.33768,-10.058994 -26.33768,-19.580489 0,-8.369699 13.89831,-14.05188 13.89831,-14.05188 7.83221,0 16.2019,4.4536 21.11622,1.61251 l 1.22858,1.689298 -12.90008,10.289358 c -4.99112,0 -11.44115,-8.292917 -13.43759,-5.221466 z" style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-dasharray:330;stroke-dashoffset:330;"></path></svg></figcaption></figure></a></nav></header><main class="py-24"><div class="mx-auto max-w-3xl"><h1 class="text-3xl font-bold leading-tight text-center">My Blog</h1></div><div class="mx-auto max-w-4xl py-8 leading-8"><div><a class="underline" href="/blog/gleam-test-timeouts"><h2 class="text-xl font-bold">Test timeouts in Gleam</h2></a><span class="text-sm">2025-11-25</span><p class="text-lg">By default, Gleam tests come with a timeout of 5 seconds. It&#39;s not as easy as you might think to remove it.</p></div></div></main><footer class="right-5 bottom-4 fixed">Made with <a class="underline" href="https://lustre.build" target="_blank">Lustre</a> and <a class="underline" href="https://gleam.run" target="_blank">Gleam</a><br>Source: <a class="underline" href="https://github.com/GearsDatapacks/GearsDatapacks.github.io" target="_blank">GitHub</a></footer></body></html>

0 commit comments

Comments
 (0)