|
21 | 21 | various parts of how EUnit runs. It's detailed in the EUnit documentation, |
22 | 22 | 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 |
23 | 23 | in order to configure it. In Erlang syntax, here is how you would configure the |
24 | | -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>-></span> |
| 24 | +timeout for your test, to be 60 seconds instead of 5:</p><div class="codeblock"><button class="copy-button" onclick="navigator.clipboard.writeText(`something_test_() -> |
| 25 | + {timeout, 60, fun () -> |
| 26 | + ... % The actual body of your test |
| 27 | + }. |
| 28 | +`)">copy</button><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>-></span> |
25 | 29 | <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>-></span> |
26 | 30 | <span class=hl-punctuation>...</span> <span class=hl-comment>% The actual body of your test |
27 | 31 | </span> <span class=hl-punctuation>}</span><span class=hl-punctuation>.</span> |
28 | | -</span></code></pre><p>So, what does this translate to Gleam? Well, there are actually two ways to do it. |
| 32 | +</span></code></pre></div><p>So, what does this translate to Gleam? Well, there are actually two ways to do it. |
29 | 33 | 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> |
30 | | -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> |
| 34 | +library. This creates basically the same as the Erlang code:</p><div class="codeblock"><button class="copy-button" onclick="navigator.clipboard.writeText(`import gleam/erlang/atom |
| 35 | +
|
| 36 | +pub fn something_test_() { |
| 37 | + #(atom.create("timeout"), 60, fn() { |
| 38 | + ... // The actual body of your test |
| 39 | + }) |
| 40 | +} |
| 41 | +`)">copy</button><pre><code data-lang="gleam"><span><span class=hl-keyword>import</span> <span class=hl-module>gleam/erlang/atom</span> |
31 | 42 |
|
32 | 43 | <span class=hl-keyword>pub</span> <span class=hl-keyword>fn</span> <span class=hl-function>something_test_</span>() { |
33 | 44 | #(<span class=hl-module>atom</span>.<span class=hl-function>create</span>(<span class=hl-string>"timeout"</span>), <span class=hl-number>60</span>, <span class=hl-keyword>fn</span>() { |
34 | 45 | ... <span class=hl-comment>// The actual body of your test</span> |
35 | 46 | }) |
36 | 47 | } |
37 | | -</span></code></pre><p>This works, but it has a couple of drawbacks. Firstly, you need to pull in a new |
| 48 | +</span></code></pre></div><p>This works, but it has a couple of drawbacks. Firstly, you need to pull in a new |
38 | 49 | library just to create this one atom, and secondly it just doesn't look very nice. |
39 | 50 | My preferred solution takes advantage of the way that custom types are represented |
40 | 51 | in Gleam.</p><p>When Gleam compiles to Erlang, Gleam custom types become an Erlang tuple tagged |
41 | 52 | with an atom. So the Gleam value <code>Ok(10)</code> becomes the Erlang <code>{ok, 10}</code>. This is |
42 | 53 | perfect for what we need, because it's the exact same format as what EUnit expects. |
43 | | -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) { |
| 54 | +Using that knowledge we can define a custom type to represent the timeout tuple:</p><div class="codeblock"><button class="copy-button" onclick="navigator.clipboard.writeText(`pub type Timeout(a) { |
| 55 | + Timeout(time: Int, function: fn() -> a) |
| 56 | +} |
| 57 | +`)">copy</button><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) { |
44 | 58 | <span class=hl-variant>Timeout</span>(time: <span class=hl-variant>Int</span>, function: <span class=hl-keyword>fn</span>() -> a) |
45 | 59 | } |
46 | | -</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 |
| 60 | +</span></code></pre></div><p>You can use <code>Float</code> instead for <code>time</code> here, but I find I rarely need to set my |
47 | 61 | timeout to a fraction of a second, so <code>Int</code> is more convenient. Now, we can take |
48 | 62 | advantage of Gleam's <a class="underline" href="https://tour.gleam.run/advanced-features/use/" target="_blank"><code>use</code> syntax</a>, |
49 | 63 | which allows us to turn a call to a higher order function — where we pass an anonymous |
50 | 64 | function, like we did in the previous example — into a flattened statement. |
51 | | -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) { |
| 65 | +We now get a much nicer looking test:</p><div class="codeblock"><button class="copy-button" onclick="navigator.clipboard.writeText(`pub type Timeout(a) { |
| 66 | + Timeout(time: Int, function: fn() -> a) |
| 67 | +} |
| 68 | +
|
| 69 | +pub fn something_test_() { |
| 70 | + use <- Timeout(60) |
| 71 | + ... // The actual body of your test |
| 72 | +} |
| 73 | +`)">copy</button><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) { |
52 | 74 | <span class=hl-variant>Timeout</span>(time: <span class=hl-variant>Int</span>, function: <span class=hl-keyword>fn</span>() -> a) |
53 | 75 | } |
54 | 76 |
|
55 | 77 | <span class=hl-keyword>pub</span> <span class=hl-keyword>fn</span> <span class=hl-function>something_test_</span>() { |
56 | 78 | <span class=hl-keyword>use</span> <- <span class=hl-variant>Timeout</span>(<span class=hl-number>60</span>) |
57 | 79 | ... <span class=hl-comment>// The actual body of your test</span> |
58 | 80 | } |
59 | | -</span></code></pre><p>Great! We've successfully worked around EUnit's annoying timeout. Job done! But |
| 81 | +</span></code></pre></div><p>Great! We've successfully worked around EUnit's annoying timeout. Job done! But |
60 | 82 | 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 |
61 | 83 | JavaScript target? Well, the good news is that it doesn't have a timeout like on |
62 | 84 | the Erlang target, but the bad news is that it doesn't support test generators. |
63 | 85 | So if all your tests are called <code>*_test_</code>, none of them are going to run on |
64 | 86 | the JavaScript target.</p><p>The only real way to get around this is to write a bit of extra boilerplate for |
65 | 87 | all of your tests. You need to write a test that runs only on JavaScript, that |
66 | 88 | calls the same code as the Erlang test. There's only really one way achieve this, |
67 | | -with the <code>@target</code> attribute, and it's not pretty. Here's an example:</p><pre><code data-lang="gleam"><span><span class=hl-comment>// This runs on Erlang</span> |
| 89 | +with the <code>@target</code> attribute, and it's not pretty. Here's an example:</p><div class="codeblock"><button class="copy-button" onclick="navigator.clipboard.writeText(`// This runs on Erlang |
| 90 | +pub fn something_test_() { |
| 91 | + use <- Timeout(60) |
| 92 | + ... // The actual body of your test |
| 93 | +} |
| 94 | +
|
| 95 | +// This runs on JavaScript |
| 96 | +@target(javascript) |
| 97 | +pub fn something_test() { |
| 98 | + something_test_().function() |
| 99 | +} |
| 100 | +`)">copy</button><pre><code data-lang="gleam"><span><span class=hl-comment>// This runs on Erlang</span> |
68 | 101 | <span class=hl-keyword>pub</span> <span class=hl-keyword>fn</span> <span class=hl-function>something_test_</span>() { |
69 | 102 | <span class=hl-keyword>use</span> <- <span class=hl-variant>Timeout</span>(<span class=hl-number>60</span>) |
70 | 103 | ... <span class=hl-comment>// The actual body of your test</span> |
|
75 | 108 | <span class=hl-keyword>pub</span> <span class=hl-keyword>fn</span> <span class=hl-function>something_test</span>() { |
76 | 109 | <span class=hl-function>something_test_</span>().<span class=hl-function>function</span>() |
77 | 110 | } |
78 | | -</span></code></pre><p>The <code>@target</code> attribute is deprecated, and you shouldn't use it really, but there |
| 111 | +</span></code></pre></div><p>The <code>@target</code> attribute is deprecated, and you shouldn't use it really, but there |
79 | 112 | isn't any alternative for this specific case. Hopefully soon we will have an |
80 | 113 | alternative, both to <code>@target</code>, and to this problem of different behaviour in the |
81 | 114 | 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's not |
|
0 commit comments