EVOLUTION-MANAGER
Edit File: third-edition.html
<!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>testthat 3e</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 { display: inline-block; 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 { 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">testthat 3e</h1> <p>testthat 3.0.0 introduces the idea of an “edition” of testthat. An edition is a bundle of behaviours that you have to explicitly choose to use, allowing us to make otherwise backward incompatible changes. This is particularly important for testthat since it has a very large number of packages that use it (almost 5,000 at last count). Choosing to use the 3rd edition allows you to use our latest recommendations for ongoing and new work, while historical packages continue to use the old behaviour.</p> <p>(We don’t anticipate creating new editions very often, and they’ll always be matched with major version, i.e. if there’s another edition, it’ll be the fourth edition and will come with testthat 4.0.0.)</p> <p>This vignette shows you how to activate the 3rd edition, introduces the main features, and discusses common challenges when upgrading a package. If you have a problem that this vignette doesn’t cover, please let me know, as it’s likely that the problem also affects others.</p> <div class="sourceCode" id="cb1"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">library</span>(testthat)</span> <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="fu">local_edition</span>(<span class="dv">3</span>)</span></code></pre></div> <div id="activating" class="section level2"> <h2>Activating</h2> <p>The usual way to activate the 3rd edition is to add a line to your <code>DESCRIPTION</code>:</p> <pre><code>Config/testthat/edition: 3</code></pre> <p>This will activate the 3rd edition for every test in your package.</p> <p>You can also control the edition used for individual tests with <code>testthat::local_edition()</code>:</p> <div class="sourceCode" id="cb3"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">test_that</span>(<span class="st">"I can use the 3rd edition"</span>, {</span> <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">local_edition</span>(<span class="dv">3</span>)</span> <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">expect_true</span>(<span class="cn">TRUE</span>)</span> <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>})</span> <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> Test passed 🎉</span></span></code></pre></div> <p>This is also useful if you’ve switched to the 3rd edition and have a couple of tests that fail. You can use <code>local_edition(2)</code> to revert back to the old behaviour, giving you some breathing room to figure out the underlying issue.</p> <div class="sourceCode" id="cb4"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">test_that</span>(<span class="st">"I want to use the 2nd edition"</span>, {</span> <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">local_edition</span>(<span class="dv">2</span>)</span> <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">expect_true</span>(<span class="cn">TRUE</span>)</span> <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>})</span> <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> Test passed 🎊</span></span></code></pre></div> </div> <div id="changes" class="section level2"> <h2>Changes</h2> <p>There are three major changes in the 3rd edition:</p> <ul> <li><p>A number of outdated functions are now <strong>deprecated</strong>, so you’ll be warned about them every time you run your tests (but they won’t cause <code>R CMD check</code> to fail).</p></li> <li><p>testthat no longer silently swallows <strong>messages</strong>; you now need to deliberately handle them.</p></li> <li><p><code>expect_equal()</code> and <code>expect_identical()</code> now use the <a href="https://waldo.r-lib.org/"><strong>waldo</strong></a> package instead of <code>identical()</code> and <code>all.equal()</code>. This makes them more consistent and provides an enhanced display of differences when a test fails.</p></li> </ul> <div id="deprecations" class="section level3"> <h3>Deprecations</h3> <p>A number of outdated functions have been deprecated. Most of these functions have not been recommended for a number of years, but before the introduction of the edition idea, I didn’t have a good way of preventing people from using them without breaking a lot of code on CRAN.</p> <ul> <li><p><code>context()</code> is formally deprecated. testthat has been moving away from <code>context()</code> in favour of file names for quite some time, and now you’ll be strongly encouraged remove these calls from your tests.</p></li> <li><p><code>expect_is()</code> is deprecated in favour of the more specific <code>expect_type()</code>, <code>expect_s3_class()</code>, and <code>expect_s4_class()</code>. This ensures that you check the expected class along with the expected OO system.</p></li> <li><p>The very old <code>expect_that()</code> syntax is now deprecated. This was an overly clever API that I regretted even before the release of testthat 1.0.0.</p></li> <li><p><code>expect_equivalent()</code> has been deprecated since it is now equivalent (HA HA) to <code>expect_equal(ignore_attr = TRUE)</code>. The main difference is that it won’t ignore names; so you’ll need an explicit <code>unname()</code> if you deliberately want to ignore names.</p></li> <li><p><code>setup()</code> and <code>teardown()</code> are deprecated in favour of test fixtures. See <code>vignette("test-fixtures")</code> for details.</p></li> <li><p><code>expect_known_output()</code>, <code>expect_known_value()</code>, <code>expect_known_hash()</code>, and <code>expect_equal_to_reference()</code> are all deprecated in favour of <code>expect_snapshot_output()</code> and <code>expect_snapshot_value()</code>.</p></li> <li><p><code>with_mock()</code> and <code>local_mock()</code> are deprecated; please use the <a href="https://krlmlr.github.io/mockr/">mockr</a> or <a href="https://github.com/r-lib/mockery#mockery">mockery</a> instead.</p></li> </ul> <p>Fixing these deprecation warnings should be straightforward.</p> </div> <div id="warnings" class="section level3"> <h3>Warnings</h3> <p>In the second edition, <code>expect_warning()</code> swallows all warnings regardless of whether or not they match the <code>regexp</code> or <code>class</code>:</p> <div class="sourceCode" id="cb5"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>f <span class="ot"><-</span> <span class="cf">function</span>() {</span> <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">warning</span>(<span class="st">"First warning"</span>)</span> <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">warning</span>(<span class="st">"Second warning"</span>)</span> <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">warning</span>(<span class="st">"Third warning"</span>)</span> <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>}</span> <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a></span> <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="fu">local_edition</span>(<span class="dv">2</span>)</span> <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="fu">expect_warning</span>(<span class="fu">f</span>(), <span class="st">"First"</span>)</span></code></pre></div> <p>In the third edition, <code>expect_warning()</code> captures at most one warning so the others will bubble up:</p> <div class="sourceCode" id="cb6"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="fu">local_edition</span>(<span class="dv">3</span>)</span> <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="fu">expect_warning</span>(<span class="fu">f</span>(), <span class="st">"First"</span>)</span> <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="co">#> Warning in f(): Second warning</span></span> <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="co">#> Warning in f(): Third warning</span></span></code></pre></div> <p>You can either add additional expectations to catch these warnings, or silence them all with <code>suppressWarnings()</code>:</p> <div class="sourceCode" id="cb7"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="fu">f</span>() <span class="sc">%>%</span> </span> <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">expect_warning</span>(<span class="st">"First"</span>) <span class="sc">%>%</span> </span> <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">expect_warning</span>(<span class="st">"Second"</span>) <span class="sc">%>%</span> </span> <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">expect_warning</span>(<span class="st">"Third"</span>)</span> <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a></span> <span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="fu">f</span>() <span class="sc">%>%</span> </span> <span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">expect_warning</span>(<span class="st">"First"</span>) <span class="sc">%>%</span> </span> <span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">suppressWarnings</span>()</span></code></pre></div> <p>Alternatively, you might want to capture them all in a snapshot test:</p> <div class="sourceCode" id="cb8"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="fu">test_that</span>(<span class="st">"f() produces expected outputs/messages/warnings"</span>, {</span> <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">expect_snapshot</span>(<span class="fu">f</span>()) </span> <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>})</span> <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="co">#> Can't compare snapshot to reference when testing interactively.</span></span> <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> i Run `devtools::test()` or `testthat::test_file()` to see changes.</span></span> <span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="co">#> i Current value: Code f() Warning <simpleWarning> First warning Second warning Third warning</span></span> <span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="co">#> ── Skip ('/Users/hadleywickham/Documents/rmarkdown/rmarkdown/R/render.R:762'): f() produces expected outputs/messages/warnings ──</span></span> <span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a><span class="co">#> Reason: empty test</span></span></code></pre></div> <p>The same principle also applies to <code>expect_message()</code>, but message handling has changed in a more radical way, as described next.</p> </div> <div id="messages" class="section level3"> <h3>Messages</h3> <p>For reasons that I can no longer remember, testthat silently ignores all messages. This is inconsistent with other types of output, so as of the 3rd edition, they now bubble up to your test results. You’ll have to explicit ignore them with <code>suppressMessages()</code>, or if they’re important, test for their presence with <code>expect_message()</code>.</p> </div> <div id="waldo" class="section level3"> <h3>waldo</h3> <p>Probably the biggest day-to-day difference (and the biggest reason to upgrade!) is the use of <a href="https://waldo.r-lib.org/reference/compare.html"><code>waldo::compare()</code></a> inside of <code>expect_equal()</code> and <code>expect_identical()</code>. The goal of waldo is to find and concisely describe the difference between a pair of R objects, and it’s designed specifically to help you figure out what’s gone wrong in your unit tests.</p> <div class="sourceCode" id="cb9"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>f1 <span class="ot"><-</span> <span class="fu">factor</span>(letters[<span class="dv">1</span><span class="sc">:</span><span class="dv">3</span>])</span> <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>f2 <span class="ot"><-</span> <span class="fu">ordered</span>(letters[<span class="dv">1</span><span class="sc">:</span><span class="dv">3</span>], <span class="at">levels =</span> letters[<span class="dv">1</span><span class="sc">:</span><span class="dv">4</span>])</span> <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="fu">local_edition</span>(<span class="dv">2</span>)</span> <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="fu">expect_equal</span>(f1, f2)</span> <span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error: `f1` not equal to `f2`.</span></span> <span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="co">#> Attributes: < Component "class": Lengths (1, 2) differ (string compare on first 1) ></span></span> <span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a><span class="co">#> Attributes: < Component "class": 1 string mismatch ></span></span> <span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a><span class="co">#> Attributes: < Component "levels": Lengths (3, 4) differ (string compare on first 3) ></span></span> <span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a></span> <span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a><span class="fu">local_edition</span>(<span class="dv">3</span>)</span> <span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a><span class="fu">expect_equal</span>(f1, f2)</span> <span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error: `f1` (`actual`) not equal to `f2` (`expected`).</span></span> <span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a><span class="co">#> </span></span> <span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a><span class="co">#> `class(actual)`: "factor" </span></span> <span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a><span class="co">#> `class(expected)`: "ordered" "factor"</span></span> <span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a><span class="co">#> </span></span> <span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a><span class="co">#> `levels(actual)`: "a" "b" "c" </span></span> <span id="cb9-19"><a href="#cb9-19" aria-hidden="true" tabindex="-1"></a><span class="co">#> `levels(expected)`: "a" "b" "c" "d"</span></span></code></pre></div> <p>waldo looks even better in your console because it carefully uses colours to help highlight the differences.</p> <p>The use of waldo also makes precise the difference between <code>expect_equal()</code> and <code>expect_identical()</code>: <code>expect_equal()</code> sets <code>tolerance</code> so that waldo will ignore small numerical differences arising from floating point computation. Otherwise the functions are identical (HA HA).</p> <p>This change is likely to result in the most work during an upgrade, because waldo can give slightly different results to both <code>identical()</code> and <code>all.equal()</code> in moderately common situations. I believe on the whole the differences are meaningful and useful, so you’ll need to handle them by tweaking your tests. The following changes are most likely to affect you:</p> <ul> <li><p><code>expect_equal()</code> previously ignored the environments of formulas and functions. This is most like to arise if you are testing models. It’s worth thinking about what the correct values should be, but if that is to annoying you can opt out of the comparison with <code>ignore_function_env</code> or <code>ignore_formula_env</code>.</p></li> <li><p><code>expect_equal()</code> used a combination of <code>all.equal()</code> and a home-grown <code>testthat::compare()</code> which unfortunately used a slightly different definition of tolerance. Now <code>expect_equal()</code> always uses the same definition of tolerance everywhere, which may require tweaks to your exising tolerance values.</p></li> <li><p><code>expect_equal()</code> previously ignored timezone differences when one object had the current timezone set implicitly (with <code>""</code>) and the other had it set explicitly:</p> <div class="sourceCode" id="cb10"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>dt1 <span class="ot"><-</span> dt2 <span class="ot"><-</span> <span class="fu">ISOdatetime</span>(<span class="dv">2020</span>, <span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">0</span>)</span> <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="fu">attr</span>(dt1, <span class="st">"tzone"</span>) <span class="ot"><-</span> <span class="st">""</span></span> <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="fu">attr</span>(dt2, <span class="st">"tzone"</span>) <span class="ot"><-</span> <span class="fu">Sys.timezone</span>()</span> <span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="fu">local_edition</span>(<span class="dv">2</span>)</span> <span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="fu">expect_equal</span>(dt1, dt2)</span> <span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a></span> <span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a><span class="fu">local_edition</span>(<span class="dv">3</span>)</span> <span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a><span class="fu">expect_equal</span>(dt1, dt2)</span> <span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error: `dt1` (`actual`) not equal to `dt2` (`expected`).</span></span> <span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a><span class="co">#> </span></span> <span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a><span class="co">#> `attr(actual, 'tzone')`: "" </span></span> <span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a><span class="co">#> `attr(expected, 'tzone')`: "America/Chicago"</span></span></code></pre></div></li> </ul> </div> <div id="reproducible-output" class="section level3"> <h3>Reproducible output</h3> <p>In the third edition, <code>test_that()</code> automatically calls <code>local_reproducible_output()</code> which automatically sets a number of options and environment variables to ensure output is as reproducible across systems. This includes setting:</p> <ul> <li><p><code>options(crayon.enabled = FALSE)</code> and <code>options(cli.unicode = FALSE)</code> so that the crayon and cli packages produce raw ASCII output.</p></li> <li><p><code>Sys.setLocale("LC_COLLATE" = "C")</code> so that sorting a character vector returns the same order regardless of the system language.</p></li> <li><p><code>options(width = 80)</code> so print methods always generate the same output regardless of your actual console width.</p></li> </ul> <p>See the documentation for more details.</p> </div> </div> <div id="upgrading" class="section level2"> <h2>Upgrading</h2> <p>The changes lend themselves to the following workflow for upgrading from the 2nd to the 3rd edition:</p> <ol style="list-style-type: decimal"> <li>Activate edition 3. You can let <a href="https://usethis.r-lib.org/reference/use_testthat.html"><code>usethis::use_testthat(3)</code></a> do this for you.</li> <li>Remove or replace deprecated functions, going over the list of above.</li> <li>If your output got noisy, quiet things down by either capturing or suppressing warnings and messages.</li> <li>Inspect test outputs if objects are not “all equal” anymore.</li> </ol> </div> <div id="alternatives" class="section level2"> <h2>Alternatives</h2> <p>You might wonder why we came up with the idea of an “edition”, rather than creating a new package like testthat3. We decided against making a new package because the 2nd and 3rd edition share a very large amount of code, so making a new package would have substantially increased the maintenance burden: the majority of bugs would’ve needed to be fixed in two places.</p> <p>If you’re a programmer in other languages, you might wonder why we can’t rely on <a href="https://semver.org">semantic versioning</a>. The main reason is that CRAN checks all packages that use testthat with the latest version of testthat, so simply incrementing the major version number doesn’t actually help with reducing R CMD check failures on CRAN.</p> </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>