File: test-fixtures.html

package info (click to toggle)
r-cran-testthat 3.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,452 kB
  • sloc: cpp: 9,261; ansic: 37; sh: 14; makefile: 5
file content (777 lines) | stat: -rw-r--r-- 41,551 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta http-equiv="X-UA-Compatible" content="IE=EDGE" />

<meta name="viewport" content="width=device-width, initial-scale=1" />



<title>Test fixtures</title>

<script>// Pandoc 2.9 adds attributes on both header and div. We remove the former (to
// be compatible with the behavior of Pandoc < 2.8).
document.addEventListener('DOMContentLoaded', function(e) {
  var hs = document.querySelectorAll("div.section[class*='level'] > :first-child");
  var i, h, a;
  for (i = 0; i < hs.length; i++) {
    h = hs[i];
    if (!/^h[1-6]$/i.test(h.tagName)) continue;  // it should be a header h1-h6
    a = h.attributes;
    while (a.length > 0) h.removeAttribute(a[0].name);
  }
});
</script>

<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>



<style type="text/css">
code {
white-space: pre;
}
.sourceCode {
overflow: visible;
}
</style>
<style type="text/css" data-origin="pandoc">
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { display: inline-block; text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } 
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } 
code span.at { color: #7d9029; } 
code span.bn { color: #40a070; } 
code span.bu { color: #008000; } 
code span.cf { color: #007020; font-weight: bold; } 
code span.ch { color: #4070a0; } 
code span.cn { color: #880000; } 
code span.co { color: #60a0b0; font-style: italic; } 
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } 
code span.do { color: #ba2121; font-style: italic; } 
code span.dt { color: #902000; } 
code span.dv { color: #40a070; } 
code span.er { color: #ff0000; font-weight: bold; } 
code span.ex { } 
code span.fl { color: #40a070; } 
code span.fu { color: #06287e; } 
code span.im { color: #008000; font-weight: bold; } 
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } 
code span.kw { color: #007020; font-weight: bold; } 
code span.op { color: #666666; } 
code span.ot { color: #007020; } 
code span.pp { color: #bc7a00; } 
code span.sc { color: #4070a0; } 
code span.ss { color: #bb6688; } 
code span.st { color: #4070a0; } 
code span.va { color: #19177c; } 
code span.vs { color: #4070a0; } 
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } 
</style>
<script>
// apply pandoc div.sourceCode style to pre.sourceCode instead
(function() {
  var sheets = document.styleSheets;
  for (var i = 0; i < sheets.length; i++) {
    if (sheets[i].ownerNode.dataset["origin"] !== "pandoc") continue;
    try { var rules = sheets[i].cssRules; } catch (e) { continue; }
    var j = 0;
    while (j < rules.length) {
      var rule = rules[j];
      // check if there is a div.sourceCode rule
      if (rule.type !== rule.STYLE_RULE || rule.selectorText !== "div.sourceCode") {
        j++;
        continue;
      }
      var style = rule.style.cssText;
      // check if color or background-color is set
      if (rule.style.color === '' && rule.style.backgroundColor === '') {
        j++;
        continue;
      }
      // replace div.sourceCode by a pre.sourceCode rule
      sheets[i].deleteRule(j);
      sheets[i].insertRule('pre.sourceCode{' + style + '}', j);
    }
  }
})();
</script>




<style type="text/css">body {
background-color: #fff;
margin: 1em auto;
max-width: 700px;
overflow: visible;
padding-left: 2em;
padding-right: 2em;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.35;
}
#TOC {
clear: both;
margin: 0 0 10px 10px;
padding: 4px;
width: 400px;
border: 1px solid #CCCCCC;
border-radius: 5px;
background-color: #f6f6f6;
font-size: 13px;
line-height: 1.3;
}
#TOC .toctitle {
font-weight: bold;
font-size: 15px;
margin-left: 5px;
}
#TOC ul {
padding-left: 40px;
margin-left: -1.5em;
margin-top: 5px;
margin-bottom: 5px;
}
#TOC ul ul {
margin-left: -2em;
}
#TOC li {
line-height: 16px;
}
table {
margin: 1em auto;
border-width: 1px;
border-color: #DDDDDD;
border-style: outset;
border-collapse: collapse;
}
table th {
border-width: 2px;
padding: 5px;
border-style: inset;
}
table td {
border-width: 1px;
border-style: inset;
line-height: 18px;
padding: 5px 5px;
}
table, table th, table td {
border-left-style: none;
border-right-style: none;
}
table thead, table tr.even {
background-color: #f7f7f7;
}
p {
margin: 0.5em 0;
}
blockquote {
background-color: #f6f6f6;
padding: 0.25em 0.75em;
}
hr {
border-style: solid;
border: none;
border-top: 1px solid #777;
margin: 28px 0;
}
dl {
margin-left: 0;
}
dl dd {
margin-bottom: 13px;
margin-left: 13px;
}
dl dt {
font-weight: bold;
}
ul {
margin-top: 0;
}
ul li {
list-style: circle outside;
}
ul ul {
margin-bottom: 0;
}
pre, code {
background-color: #f7f7f7;
border-radius: 3px;
color: #333;
white-space: pre-wrap; 
}
pre {
border-radius: 3px;
margin: 5px 0px 10px 0px;
padding: 10px;
}
pre:not([class]) {
background-color: #f7f7f7;
}
code {
font-family: Consolas, Monaco, 'Courier New', monospace;
font-size: 85%;
}
p > code, li > code {
padding: 2px 0px;
}
div.figure {
text-align: center;
}
img {
background-color: #FFFFFF;
padding: 2px;
border: 1px solid #DDDDDD;
border-radius: 3px;
border: 1px solid #CCCCCC;
margin: 0 5px;
}
h1 {
margin-top: 0;
font-size: 35px;
line-height: 40px;
}
h2 {
border-bottom: 4px solid #f7f7f7;
padding-top: 10px;
padding-bottom: 2px;
font-size: 145%;
}
h3 {
border-bottom: 2px solid #f7f7f7;
padding-top: 10px;
font-size: 120%;
}
h4 {
border-bottom: 1px solid #f7f7f7;
margin-left: 8px;
font-size: 105%;
}
h5, h6 {
border-bottom: 1px solid #ccc;
font-size: 105%;
}
a {
color: #0033dd;
text-decoration: none;
}
a:hover {
color: #6666ff; }
a:visited {
color: #800080; }
a:visited:hover {
color: #BB00BB; }
a[href^="http:"] {
text-decoration: underline; }
a[href^="https:"] {
text-decoration: underline; }

code > span.kw { color: #555; font-weight: bold; } 
code > span.dt { color: #902000; } 
code > span.dv { color: #40a070; } 
code > span.bn { color: #d14; } 
code > span.fl { color: #d14; } 
code > span.ch { color: #d14; } 
code > span.st { color: #d14; } 
code > span.co { color: #888888; font-style: italic; } 
code > span.ot { color: #007020; } 
code > span.al { color: #ff0000; font-weight: bold; } 
code > span.fu { color: #900; font-weight: bold; } 
code > span.er { color: #a61717; background-color: #e3d2d2; } 
</style>




</head>

<body>




<h1 class="title toc-ignore">Test fixtures</h1>



<div id="test-hygiene" class="section level2">
<h2>Test hygiene</h2>
<blockquote>
<p>Take nothing but memories, leave nothing but footprints.</p>
<p>― Chief Si’ahl</p>
</blockquote>
<p>Ideally, a test should leave the world exactly as it found it. But
you often need to make some changes in order to exercise every part of
your code:</p>
<ul>
<li>Create a file or directory</li>
<li>Create a resource on an external system</li>
<li>Set an R option</li>
<li>Set an environment variable</li>
<li>Change working directory</li>
<li>Change an aspect of the tested package’s state</li>
</ul>
<p>How can you clean up these changes to get back to a clean slate?
Scrupulous attention to cleanup is more than just courtesy or being
fastidious. It is also self-serving. The state of the world after test
<code>i</code> is the starting state for test <code>i + 1</code>. Tests
that change state willy-nilly eventually end up interfering with each
other in ways that can be very difficult to debug.</p>
<p>Most tests are written with an implicit assumption about the starting
state, usually whatever <em>tabula rasa</em> means for the target domain
of your package. If you accumulate enough sloppy tests, you will
eventually find yourself asking the programming equivalent of questions
like “Who forgot to turn off the oven?” and “Who didn’t clean up after
the dog?”.</p>
<p>It’s also important that your setup and cleanup is easy to use when
working interactively. When a test fails, you want to be able to quickly
recreate the exact environment in which the test is run so you can
interactively experiment to figure out what went wrong.</p>
<p>This article introduces a powerful technique that allows you to solve
both problems: <strong>test fixtures</strong>. We’ll begin with an
introduction to the tools that make fixtures possible, then talk about
exactly what a test fixture is, and show a few examples.</p>
<p>Much of this vignette is derived from <a href="https://www.tidyverse.org/blog/2020/04/self-cleaning-test-fixtures/" class="uri">https://www.tidyverse.org/blog/2020/04/self-cleaning-test-fixtures/</a>;
if this is your first encounter with <code>on.exit()</code> or
<code>withr::defer()</code>, I’d recommend starting with that blog as it
gives a gentler introduction. This vignette moves a little faster since
it’s designed as more of a reference doc.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb1-1"><a href="#cb1-1" tabindex="-1"></a><span class="fu">library</span>(testthat)</span></code></pre></div>
</div>
<div id="foundations" class="section level2">
<h2>Foundations</h2>
<p>Before we can talk about test fixtures, we need to lay some
foundations to help you understand how they work. We’ll motivate the
discussion with a <code>sloppy()</code> function that prints a number
with a specific number of significant digits by adjusting an R
option:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb2-1"><a href="#cb2-1" tabindex="-1"></a>sloppy <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb2-2"><a href="#cb2-2" tabindex="-1"></a>  <span class="fu">options</span>(<span class="at">digits =</span> sig_digits)</span>
<span id="cb2-3"><a href="#cb2-3" tabindex="-1"></a>  <span class="fu">print</span>(x)</span>
<span id="cb2-4"><a href="#cb2-4" tabindex="-1"></a>}</span>
<span id="cb2-5"><a href="#cb2-5" tabindex="-1"></a></span>
<span id="cb2-6"><a href="#cb2-6" tabindex="-1"></a>pi</span>
<span id="cb2-7"><a href="#cb2-7" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span>
<span id="cb2-8"><a href="#cb2-8" tabindex="-1"></a><span class="fu">sloppy</span>(pi, <span class="dv">2</span>)</span>
<span id="cb2-9"><a href="#cb2-9" tabindex="-1"></a><span class="co">#&gt; [1] 3.1</span></span>
<span id="cb2-10"><a href="#cb2-10" tabindex="-1"></a>pi</span>
<span id="cb2-11"><a href="#cb2-11" tabindex="-1"></a><span class="co">#&gt; [1] 3.1</span></span></code></pre></div>
<p>Notice how <code>pi</code> prints differently before and after the
call to <code>sloppy()</code>. Calling <code>sloppy()</code> has a side
effect: it changes the <code>digits</code> option globally, not just
within its own scope of operations. This is what we want to avoid<a href="#fn1" class="footnote-ref" id="fnref1"><sup>1</sup></a>.</p>
<div id="on.exit" class="section level3">
<h3><code>on.exit()</code></h3>
<p>The first function you need to know about is base R’s
<code>on.exit()</code>. <code>on.exit()</code> calls the code to
supplied to its first argument when the current function exits,
regardless of whether it returns a value or errors. You can use
<code>on.exit()</code> to clean up after yourself by ensuring that every
mess-making function call is paired with an <code>on.exit()</code> call
that cleans up.</p>
<p>We can use this idea to turn <code>sloppy()</code> into
<code>neat()</code>:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb3-1"><a href="#cb3-1" tabindex="-1"></a>neat <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb3-2"><a href="#cb3-2" tabindex="-1"></a>  op <span class="ot">&lt;-</span> <span class="fu">options</span>(<span class="at">digits =</span> sig_digits)</span>
<span id="cb3-3"><a href="#cb3-3" tabindex="-1"></a>  <span class="fu">on.exit</span>(<span class="fu">options</span>(op), <span class="at">add =</span> <span class="cn">TRUE</span>, <span class="at">after =</span> <span class="cn">FALSE</span>)</span>
<span id="cb3-4"><a href="#cb3-4" tabindex="-1"></a>  <span class="fu">print</span>(x)</span>
<span id="cb3-5"><a href="#cb3-5" tabindex="-1"></a>}</span>
<span id="cb3-6"><a href="#cb3-6" tabindex="-1"></a></span>
<span id="cb3-7"><a href="#cb3-7" tabindex="-1"></a>pi</span>
<span id="cb3-8"><a href="#cb3-8" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span>
<span id="cb3-9"><a href="#cb3-9" tabindex="-1"></a><span class="fu">neat</span>(pi, <span class="dv">2</span>)</span>
<span id="cb3-10"><a href="#cb3-10" tabindex="-1"></a><span class="co">#&gt; [1] 3.1</span></span>
<span id="cb3-11"><a href="#cb3-11" tabindex="-1"></a>pi</span>
<span id="cb3-12"><a href="#cb3-12" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span></code></pre></div>
<p>Here we make use of a useful pattern <code>options()</code>
implements: when you call <code>options(digits = sig_digits)</code> it
both sets the <code>digits</code> option <em>and</em> (invisibly)
returns the previous value of digits. We can then use that value to
restore the previous options.</p>
<p><code>on.exit()</code> also works in tests:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb4-1"><a href="#cb4-1" tabindex="-1"></a><span class="fu">test_that</span>(<span class="st">&quot;can print one digit of pi&quot;</span>, {</span>
<span id="cb4-2"><a href="#cb4-2" tabindex="-1"></a>  op <span class="ot">&lt;-</span> <span class="fu">options</span>(<span class="at">digits =</span> <span class="dv">1</span>)</span>
<span id="cb4-3"><a href="#cb4-3" tabindex="-1"></a>  <span class="fu">on.exit</span>(<span class="fu">options</span>(op), <span class="at">add =</span> <span class="cn">TRUE</span>, <span class="at">after =</span> <span class="cn">FALSE</span>)</span>
<span id="cb4-4"><a href="#cb4-4" tabindex="-1"></a>  </span>
<span id="cb4-5"><a href="#cb4-5" tabindex="-1"></a>  <span class="fu">expect_output</span>(<span class="fu">print</span>(pi), <span class="st">&quot;3&quot;</span>)</span>
<span id="cb4-6"><a href="#cb4-6" tabindex="-1"></a>})</span>
<span id="cb4-7"><a href="#cb4-7" tabindex="-1"></a><span class="co">#&gt; Test passed 🌈</span></span>
<span id="cb4-8"><a href="#cb4-8" tabindex="-1"></a>pi</span>
<span id="cb4-9"><a href="#cb4-9" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span></code></pre></div>
<p>There are three main drawbacks to <code>on.exit()</code>:</p>
<ul>
<li><p>You should always call it with <code>add = TRUE</code> and
<code>after = FALSE</code>. These ensure that the call is
<strong>added</strong> to the list of deferred tasks (instead of
replaces) and is added to the <strong>front</strong> of the stack (not
the back, so that cleanup occurs in reverse order to setup). These
arguments only matter if you’re using multiple <code>on.exit()</code>
calls, but it’s a good habit to always use them to avoid potential
problems down the road.</p></li>
<li><p>It doesn’t work outside a function or test. If you run the
following code in the global environment, you won’t get an error, but
the cleanup code will never be run:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb5-1"><a href="#cb5-1" tabindex="-1"></a>op <span class="ot">&lt;-</span> <span class="fu">options</span>(<span class="at">digits =</span> <span class="dv">1</span>)</span>
<span id="cb5-2"><a href="#cb5-2" tabindex="-1"></a><span class="fu">on.exit</span>(<span class="fu">options</span>(op), <span class="at">add =</span> <span class="cn">TRUE</span>, <span class="at">after =</span> <span class="cn">FALSE</span>)</span></code></pre></div>
<p>This is annoying when you are running tests interactively.</p></li>
<li><p>You can’t program with it; <code>on.exit()</code> always works
inside the <em>current</em> function so you can’t wrap up repeated
<code>on.exit()</code> code in a helper function.</p></li>
</ul>
<p>To resolve these drawbacks, we use <code>withr::defer()</code>.</p>
</div>
<div id="withrdefer" class="section level3">
<h3><code>withr::defer()</code></h3>
<p><code>withr::defer()</code> resolves the main drawbacks of
<code>on.exit()</code>. First, it has the behaviour we want by default;
no extra arguments needed:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb6-1"><a href="#cb6-1" tabindex="-1"></a>neat <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb6-2"><a href="#cb6-2" tabindex="-1"></a>  op <span class="ot">&lt;-</span> <span class="fu">options</span>(<span class="at">digits =</span> sig_digits)</span>
<span id="cb6-3"><a href="#cb6-3" tabindex="-1"></a>  withr<span class="sc">::</span><span class="fu">defer</span>(<span class="fu">options</span>(op))</span>
<span id="cb6-4"><a href="#cb6-4" tabindex="-1"></a>  <span class="fu">print</span>(x)</span>
<span id="cb6-5"><a href="#cb6-5" tabindex="-1"></a>}</span></code></pre></div>
<p>Second, it works when called in the global environment. Since the
global environment isn’t perishable, like a test environment is, you
have to call <code>deferred_run()</code> explicitly to execute the
deferred events. You can also clear them, without running, with
<code>deferred_clear()</code>.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb7-1"><a href="#cb7-1" tabindex="-1"></a>withr<span class="sc">::</span><span class="fu">defer</span>(<span class="fu">print</span>(<span class="st">&quot;hi&quot;</span>))</span>
<span id="cb7-2"><a href="#cb7-2" tabindex="-1"></a><span class="co">#&gt; Setting deferred event(s) on global environment.</span></span>
<span id="cb7-3"><a href="#cb7-3" tabindex="-1"></a><span class="co">#&gt;   * Execute (and clear) with `deferred_run()`.</span></span>
<span id="cb7-4"><a href="#cb7-4" tabindex="-1"></a><span class="co">#&gt;   * Clear (without executing) with `deferred_clear()`.</span></span>
<span id="cb7-5"><a href="#cb7-5" tabindex="-1"></a></span>
<span id="cb7-6"><a href="#cb7-6" tabindex="-1"></a>withr<span class="sc">::</span><span class="fu">deferred_run</span>()</span>
<span id="cb7-7"><a href="#cb7-7" tabindex="-1"></a><span class="co">#&gt; [1] &quot;hi&quot;</span></span></code></pre></div>
<p>Finally, <code>withr::defer()</code> lets you pick which function to
bind the clean up behaviour too. This makes it possible to create helper
functions.</p>
</div>
<div id="local-helpers" class="section level3">
<h3>“Local” helpers</h3>
<p>Imagine we have many functions where we want to temporarily set the
digits option. Wouldn’t it be nice if we could write a helper function
to automate? Unfortunately we can’t write a helper with
<code>on.exit()</code>:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb8-1"><a href="#cb8-1" tabindex="-1"></a>local_digits <span class="ot">&lt;-</span> <span class="cf">function</span>(sig_digits) {</span>
<span id="cb8-2"><a href="#cb8-2" tabindex="-1"></a>  op <span class="ot">&lt;-</span> <span class="fu">options</span>(<span class="at">digits =</span> sig_digits)</span>
<span id="cb8-3"><a href="#cb8-3" tabindex="-1"></a>  <span class="fu">on.exit</span>(<span class="fu">options</span>(op), <span class="at">add =</span> <span class="cn">TRUE</span>, <span class="at">after =</span> <span class="cn">FALSE</span>)</span>
<span id="cb8-4"><a href="#cb8-4" tabindex="-1"></a>}</span>
<span id="cb8-5"><a href="#cb8-5" tabindex="-1"></a>neater <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb8-6"><a href="#cb8-6" tabindex="-1"></a>  <span class="fu">local_digits</span>(<span class="dv">1</span>)</span>
<span id="cb8-7"><a href="#cb8-7" tabindex="-1"></a>  <span class="fu">print</span>(x)</span>
<span id="cb8-8"><a href="#cb8-8" tabindex="-1"></a>}</span>
<span id="cb8-9"><a href="#cb8-9" tabindex="-1"></a><span class="fu">neater</span>(pi)</span>
<span id="cb8-10"><a href="#cb8-10" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span></code></pre></div>
<p>This code doesn’t work because the cleanup happens too soon, when
<code>local_digits()</code> exits, not when <code>neat()</code>
finishes.</p>
<p>Fortunately, <code>withr::defer()</code> allows us to solve this
problem by providing an <code>envir</code> argument that allows you to
control when cleanup occurs. The exact details of how this works are
rather complicated, but fortunately there’s a common pattern you can use
without understanding all the details. Your helper function should
always have an <code>env</code> argument that defaults to
<code>parent.frame()</code>, which you pass to the second argument of
<code>defer()</code>:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb9-1"><a href="#cb9-1" tabindex="-1"></a>local_digits <span class="ot">&lt;-</span> <span class="cf">function</span>(sig_digits, <span class="at">env =</span> <span class="fu">parent.frame</span>()) {</span>
<span id="cb9-2"><a href="#cb9-2" tabindex="-1"></a>  op <span class="ot">&lt;-</span> <span class="fu">options</span>(<span class="at">digits =</span> sig_digits)</span>
<span id="cb9-3"><a href="#cb9-3" tabindex="-1"></a>  withr<span class="sc">::</span><span class="fu">defer</span>(<span class="fu">options</span>(op), env)</span>
<span id="cb9-4"><a href="#cb9-4" tabindex="-1"></a>}</span>
<span id="cb9-5"><a href="#cb9-5" tabindex="-1"></a></span>
<span id="cb9-6"><a href="#cb9-6" tabindex="-1"></a><span class="fu">neater</span>(pi)</span>
<span id="cb9-7"><a href="#cb9-7" tabindex="-1"></a><span class="co">#&gt; [1] 3</span></span></code></pre></div>
<p>Just like <code>on.exit()</code> and <code>defer()</code>, our helper
also works within tests:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb10-1"><a href="#cb10-1" tabindex="-1"></a><span class="fu">test_that</span>(<span class="st">&quot;withr lets us write custom helpers for local state manipulation&quot;</span>, {</span>
<span id="cb10-2"><a href="#cb10-2" tabindex="-1"></a>  <span class="fu">local_digits</span>(<span class="dv">1</span>)</span>
<span id="cb10-3"><a href="#cb10-3" tabindex="-1"></a>  <span class="fu">expect_output</span>(<span class="fu">print</span>(<span class="fu">exp</span>(<span class="dv">1</span>)), <span class="st">&quot;3&quot;</span>)</span>
<span id="cb10-4"><a href="#cb10-4" tabindex="-1"></a>  </span>
<span id="cb10-5"><a href="#cb10-5" tabindex="-1"></a>  <span class="fu">local_digits</span>(<span class="dv">3</span>)</span>
<span id="cb10-6"><a href="#cb10-6" tabindex="-1"></a>  <span class="fu">expect_output</span>(<span class="fu">print</span>(<span class="fu">exp</span>(<span class="dv">1</span>)), <span class="st">&quot;2.72&quot;</span>)</span>
<span id="cb10-7"><a href="#cb10-7" tabindex="-1"></a>})</span>
<span id="cb10-8"><a href="#cb10-8" tabindex="-1"></a><span class="co">#&gt; Test passed 🎊</span></span>
<span id="cb10-9"><a href="#cb10-9" tabindex="-1"></a></span>
<span id="cb10-10"><a href="#cb10-10" tabindex="-1"></a><span class="fu">print</span>(<span class="fu">exp</span>(<span class="dv">1</span>))</span>
<span id="cb10-11"><a href="#cb10-11" tabindex="-1"></a><span class="co">#&gt; [1] 2.718282</span></span></code></pre></div>
<p>We always call these helper functions <code>local_</code>; “local”
here refers to the fact that the state change persists only locally, for
the lifetime of the associated function or test.</p>
</div>
<div id="pre-existing-helpers" class="section level3">
<h3>Pre-existing helpers</h3>
<p>But before you write your own helper function, make sure to check out
the wide range of local functions already provided by withr:</p>
<table>
<thead>
<tr class="header">
<th>Do / undo this</th>
<th>withr function</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Create a file</td>
<td><code>local_file()</code></td>
</tr>
<tr class="even">
<td>Set an R option</td>
<td><code>local_options()</code></td>
</tr>
<tr class="odd">
<td>Set an environment variable</td>
<td><code>local_envvar()</code></td>
</tr>
<tr class="even">
<td>Change working directory</td>
<td><code>local_dir()</code></td>
</tr>
</tbody>
</table>
<p>We can use <code>withr::local_options()</code> to write yet another
version of <code>neater()</code>:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb11-1"><a href="#cb11-1" tabindex="-1"></a>neatest <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb11-2"><a href="#cb11-2" tabindex="-1"></a>  withr<span class="sc">::</span><span class="fu">local_options</span>(<span class="fu">list</span>(<span class="at">digits =</span> sig_digits))</span>
<span id="cb11-3"><a href="#cb11-3" tabindex="-1"></a>  <span class="fu">print</span>(x)</span>
<span id="cb11-4"><a href="#cb11-4" tabindex="-1"></a>}</span>
<span id="cb11-5"><a href="#cb11-5" tabindex="-1"></a><span class="fu">neatest</span>(pi, <span class="dv">3</span>)</span>
<span id="cb11-6"><a href="#cb11-6" tabindex="-1"></a><span class="co">#&gt; [1] 3.14</span></span></code></pre></div>
<p>Each <code>local_*()</code> function has a companion
<code>with_()</code> function, which is a nod to <code>with()</code>,
and the inspiration for withr’s name. We won’t use the
<code>with_*()</code> functions here, but you can learn more about them
at <a href="https://withr.r-lib.org">withr.r-lib.org</a>.</p>
</div>
</div>
<div id="test-fixtures" class="section level2">
<h2>Test fixtures</h2>
<p>Testing is often demonstrated with cute little tests and functions
where all the inputs and expected results can be inlined. But in real
packages, things aren’t always so simple and functions often depend on
other global state. For example, take this variant on
<code>message()</code> that only shows a message if the
<code>verbose</code> option is <code>TRUE</code>. How would you test
that setting the option does indeed silence the message?</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb12-1"><a href="#cb12-1" tabindex="-1"></a>message2 <span class="ot">&lt;-</span> <span class="cf">function</span>(...) {</span>
<span id="cb12-2"><a href="#cb12-2" tabindex="-1"></a>  <span class="cf">if</span> (<span class="sc">!</span><span class="fu">isTRUE</span>(<span class="fu">getOption</span>(<span class="st">&quot;verbose&quot;</span>))) {</span>
<span id="cb12-3"><a href="#cb12-3" tabindex="-1"></a>    <span class="fu">return</span>()</span>
<span id="cb12-4"><a href="#cb12-4" tabindex="-1"></a>  }</span>
<span id="cb12-5"><a href="#cb12-5" tabindex="-1"></a>  <span class="fu">message</span>(...)</span>
<span id="cb12-6"><a href="#cb12-6" tabindex="-1"></a>}</span></code></pre></div>
<p>In some cases, it’s possible to make the global state an explicit
argument to the function. For example, we could refactor
<code>message2()</code> to make the verbosity an explicit argument:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb13-1"><a href="#cb13-1" tabindex="-1"></a>message3 <span class="ot">&lt;-</span> <span class="cf">function</span>(..., <span class="at">verbose =</span> <span class="fu">getOption</span>(<span class="st">&quot;verbose&quot;</span>)) {</span>
<span id="cb13-2"><a href="#cb13-2" tabindex="-1"></a>  <span class="cf">if</span> (<span class="sc">!</span><span class="fu">isTRUE</span>(verbose)) {</span>
<span id="cb13-3"><a href="#cb13-3" tabindex="-1"></a>    <span class="fu">return</span>()</span>
<span id="cb13-4"><a href="#cb13-4" tabindex="-1"></a>  }</span>
<span id="cb13-5"><a href="#cb13-5" tabindex="-1"></a>  <span class="fu">message</span>(...)</span>
<span id="cb13-6"><a href="#cb13-6" tabindex="-1"></a>}</span></code></pre></div>
<p>Making external state explicit is often worthwhile, because it makes
it more clear exactly what inputs determine the outputs of your
function. But it’s simply not possible in many cases. That’s where test
fixtures come in: they allow you to temporarily change global state in
order to test your function. Test fixture is a pre-existing term in the
software engineering world (and beyond):</p>
<blockquote>
<p>A test fixture is something used to consistently test some item,
device, or piece of software.</p>
<p>— <a href="https://en.wikipedia.org/wiki/Test_fixture">Wikipedia</a></p>
</blockquote>
<p>A <strong>test fixture</strong> is just a <code>local_</code>
function that you use to change state in such a way that you can reach
inside and test parts of your code that would otherwise be challenging.
For example, here’s how you could use
<code>withr::local_options()</code> as a test fixture to test
<code>message2()</code>:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb14-1"><a href="#cb14-1" tabindex="-1"></a><span class="fu">test_that</span>(<span class="st">&quot;message2() output depends on verbose option&quot;</span>, {</span>
<span id="cb14-2"><a href="#cb14-2" tabindex="-1"></a>  withr<span class="sc">::</span><span class="fu">local_options</span>(<span class="at">verbose =</span> <span class="cn">TRUE</span>)</span>
<span id="cb14-3"><a href="#cb14-3" tabindex="-1"></a>  <span class="fu">expect_message</span>(<span class="fu">message2</span>(<span class="st">&quot;Hi!&quot;</span>))</span>
<span id="cb14-4"><a href="#cb14-4" tabindex="-1"></a>  </span>
<span id="cb14-5"><a href="#cb14-5" tabindex="-1"></a>  withr<span class="sc">::</span><span class="fu">local_options</span>(<span class="at">verbose =</span> <span class="cn">FALSE</span>)</span>
<span id="cb14-6"><a href="#cb14-6" tabindex="-1"></a>  <span class="fu">expect_message</span>(<span class="fu">message2</span>(<span class="st">&quot;Hi!&quot;</span>), <span class="cn">NA</span>)</span>
<span id="cb14-7"><a href="#cb14-7" tabindex="-1"></a>})</span>
<span id="cb14-8"><a href="#cb14-8" tabindex="-1"></a><span class="co">#&gt; Test passed 🥇</span></span></code></pre></div>
<div id="case-study-usethis" class="section level3">
<h3>Case study: usethis</h3>
<p>One place that we use test fixtures extensively is in the usethis
package (<a href="https://usethis.r-lib.org">usethis.r-lib.org</a>),
which provides functions for looking after the files and folders in R
projects, especially packages. Many of these functions only make sense
in the context of a package, which means to test them, we also have to
be working inside an R package. We need a way to quickly spin up a
minimal package in a temporary directory, then test some functions
against it, then destroy it.</p>
<p>To solve this problem we create a test fixture, which we place in
<code>R/test-helpers.R</code> so that’s it’s available for both testing
and interactive experimentation:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb15-1"><a href="#cb15-1" tabindex="-1"></a>local_create_package <span class="ot">&lt;-</span> <span class="cf">function</span>(<span class="at">dir =</span> <span class="fu">file_temp</span>(), <span class="at">env =</span> <span class="fu">parent.frame</span>()) {</span>
<span id="cb15-2"><a href="#cb15-2" tabindex="-1"></a>  old_project <span class="ot">&lt;-</span> <span class="fu">proj_get_</span>()</span>
<span id="cb15-3"><a href="#cb15-3" tabindex="-1"></a>  </span>
<span id="cb15-4"><a href="#cb15-4" tabindex="-1"></a>  <span class="co"># create new folder and package</span></span>
<span id="cb15-5"><a href="#cb15-5" tabindex="-1"></a>  <span class="fu">create_package</span>(dir, <span class="at">open =</span> <span class="cn">FALSE</span>) <span class="co"># A</span></span>
<span id="cb15-6"><a href="#cb15-6" tabindex="-1"></a>  withr<span class="sc">::</span><span class="fu">defer</span>(fs<span class="sc">::</span><span class="fu">dir_delete</span>(dir), <span class="at">envir =</span> env) <span class="co"># -A</span></span>
<span id="cb15-7"><a href="#cb15-7" tabindex="-1"></a>  </span>
<span id="cb15-8"><a href="#cb15-8" tabindex="-1"></a>  <span class="co"># change working directory</span></span>
<span id="cb15-9"><a href="#cb15-9" tabindex="-1"></a>  <span class="fu">setwd</span>(dir) <span class="co"># B</span></span>
<span id="cb15-10"><a href="#cb15-10" tabindex="-1"></a>  withr<span class="sc">::</span><span class="fu">defer</span>(<span class="fu">setwd</span>(old_project), <span class="at">envir =</span> env) <span class="co"># -B</span></span>
<span id="cb15-11"><a href="#cb15-11" tabindex="-1"></a>  </span>
<span id="cb15-12"><a href="#cb15-12" tabindex="-1"></a>  <span class="co"># switch to new usethis project</span></span>
<span id="cb15-13"><a href="#cb15-13" tabindex="-1"></a>  <span class="fu">proj_set</span>(dir) <span class="co"># C</span></span>
<span id="cb15-14"><a href="#cb15-14" tabindex="-1"></a>  withr<span class="sc">::</span><span class="fu">defer</span>(<span class="fu">proj_set</span>(old_project, <span class="at">force =</span> <span class="cn">TRUE</span>), <span class="at">envir =</span> env) <span class="co"># -C</span></span>
<span id="cb15-15"><a href="#cb15-15" tabindex="-1"></a>  </span>
<span id="cb15-16"><a href="#cb15-16" tabindex="-1"></a>  dir</span>
<span id="cb15-17"><a href="#cb15-17" tabindex="-1"></a>}</span></code></pre></div>
<p>Note that the cleanup automatically unfolds in the opposite order
from the setup. Setup is <code>A</code>, then <code>B</code>, then
<code>C</code>; cleanup is <code>-C</code>, then <code>-B</code>, then
<code>-A</code>. This is important because we must create directory
<code>dir</code> before we can make it the working directory; and we
must restore the original working directory before we can delete
<code>dir</code>; we can’t delete <code>dir</code> while it’s still the
working directory!</p>
<p><code>local_create_package()</code> is used in over 170 tests. Here’s
one example that checks that <code>usethis::use_roxygen_md()</code> does
the setup necessary to use roxygen2 in a package, with markdown support
turned on. All 3 expectations consult the DESCRIPTION file, directly or
indirectly. So it’s very convenient that
<code>local_create_package()</code> creates a minimal package, with a
valid <code>DESCRIPTION</code> file, for us to test against. And when
the test is done — poof! — the package is gone.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb16-1"><a href="#cb16-1" tabindex="-1"></a><span class="fu">test_that</span>(<span class="st">&quot;use_roxygen_md() adds DESCRIPTION fields&quot;</span>, {</span>
<span id="cb16-2"><a href="#cb16-2" tabindex="-1"></a>  pkg <span class="ot">&lt;-</span> <span class="fu">local_create_package</span>()</span>
<span id="cb16-3"><a href="#cb16-3" tabindex="-1"></a>  <span class="fu">use_roxygen_md</span>()</span>
<span id="cb16-4"><a href="#cb16-4" tabindex="-1"></a>  </span>
<span id="cb16-5"><a href="#cb16-5" tabindex="-1"></a>  <span class="fu">expect_true</span>(<span class="fu">uses_roxygen_md</span>())</span>
<span id="cb16-6"><a href="#cb16-6" tabindex="-1"></a>  <span class="fu">expect_equal</span>(desc<span class="sc">::</span><span class="fu">desc_get</span>(<span class="st">&quot;Roxygen&quot;</span>, pkg)[[<span class="dv">1</span>]], <span class="st">&quot;list(markdown = TRUE)&quot;</span>)<span class="er">)</span></span>
<span id="cb16-7"><a href="#cb16-7" tabindex="-1"></a>  <span class="fu">expect_true</span>(desc<span class="sc">::</span><span class="fu">desc_has_fields</span>(<span class="st">&quot;RoxygenNote&quot;</span>, pkg))</span>
<span id="cb16-8"><a href="#cb16-8" tabindex="-1"></a>})</span></code></pre></div>
</div>
</div>
<div id="scope" class="section level2">
<h2>Scope</h2>
<p>So far we have applied our test fixture to individual tests, but it’s
also possible to apply them to a file or package.</p>
<div id="file" class="section level3">
<h3>File</h3>
<p>If you move the <code>local_()</code> call outside of a
<code>test_that()</code> block, it will affect all tests that come after
it. This means that by calling the test fixture at the top of the file
you can change the behaviour for all tests. This has both advantages and
disadvantages:</p>
<ul>
<li><p>If you would otherwise have called the fixture in every test,
you’ve saved yourself a bunch of work and duplicate code.</p></li>
<li><p>On the downside, if your test fails and you want to recreate the
failure in an interactive environment so you can debug, you need to
remember to run all the setup code at the top of the file
first.</p></li>
</ul>
<p>Generally, I think it’s better to copy and paste test fixtures across
many tests — sure, it adds some duplication to your code, but it makes
debugging test failures so much easier.</p>
</div>
<div id="package" class="section level3">
<h3>Package</h3>
<p>To run code before any test is run, you can create a file called
<code>tests/testthat/setup.R</code>. If the code in this file needs
clean up, you can use the special <code>teardown_env()</code>:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb17-1"><a href="#cb17-1" tabindex="-1"></a><span class="co"># Run before any test</span></span>
<span id="cb17-2"><a href="#cb17-2" tabindex="-1"></a><span class="fu">write.csv</span>(<span class="st">&quot;mtcars.csv&quot;</span>, mtcars)</span>
<span id="cb17-3"><a href="#cb17-3" tabindex="-1"></a></span>
<span id="cb17-4"><a href="#cb17-4" tabindex="-1"></a><span class="co"># Run after all tests</span></span>
<span id="cb17-5"><a href="#cb17-5" tabindex="-1"></a>withr<span class="sc">::</span><span class="fu">defer</span>(<span class="fu">unlink</span>(<span class="st">&quot;mtcars.csv&quot;</span>), <span class="fu">teardown_env</span>())</span></code></pre></div>
<p>Setup code is typically best used to create external resources that
are needed by many tests. It’s best kept to a minimum because you will
have to manually run it before interactively debugging tests.</p>
</div>
</div>
<div id="other-challenges" class="section level2">
<h2>Other challenges</h2>
<p>A collection of miscellaneous problems that I don’t know where else
to describe:</p>
<ul>
<li><p>There are a few base functions that are hard to test because they
depend on state that you can’t control. One such example is
<code>interactive()</code>: there’s no way to write a test fixture that
allows you to pretend that interactive is either <code>TRUE</code> or
<code>FALSE</code>. So we now usually use
<code>rlang::is_interactive()</code> which can be controlled by the
<code>rlang_interactive</code> option.</p></li>
<li><p>If you’re using a test fixture in a function, be careful about
what you return. For example, if you write a function that does
<code>dir &lt;- create_local_package()</code> you shouldn’t return
<code>dir</code>, because after the function returns the directory will
no longer exist.</p></li>
</ul>
</div>
<div class="footnotes footnotes-end-of-document">
<hr />
<ol>
<li id="fn1"><p>Don’t worry, I’m restoring global state (specifically,
the <code>digits</code> option) behind the scenes here.<a href="#fnref1" class="footnote-back">↩︎</a></p></li>
</ol>
</div>



<!-- code folding -->


<!-- dynamically load mathjax for compatibility with self-contained -->
<script>
  (function () {
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src  = "https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
    document.getElementsByTagName("head")[0].appendChild(script);
  })();
</script>

</body>
</html>