testthat 2.1.0

  r-lib, testthat

  Hadley Wickham

We’re happy to announce that testthat 2.1.0 is now available on CRAN! testthat makes it easy to turn your existing informal tests into formal automated tests that you can rerun quickly and easily. testthat is the most popular unit-testing package for R, and is used by over 4,000 CRAN and Bioconductor packages. You can learn more about unit testing at https://r-pkgs.org/tests.html.

Install the latest version of testthat with:

install.packages("testthat")

testthat 2.1.0 mostly contains a large number of minor improvements and bug fixes, as described in the release notes. This blog post discusses the bigger improvements:

  • context()s are now optional.

  • Two new expectations: expect_invisible() and expect_mapequal().

context() is now optional

The biggest change in this version is that context() is now optional, and in fact we recommend that you no longer use it. context() made sense with the original design of testthat, which supported a very flexible mapping between your R code and the corresponding tests.

Now, however, we have a stronger recommendation: organise the files in tests/testthat in the same way that you organise the files in R/, so that tests for code in R/my-file.R live in tests/testthat-my-file.R. (This begs the question of how you should organise your R/ directory, which we don’t have a good answer for yet, but at least you only need to struggle to organise one directory). With this convention, context() tends to end up duplicating the file name, causing needless hassle if you reorganise your code structure.

The convention that every file in R/ has a corresponding file in tests/testthat (and vice versa) is used by two other helpful functions:

  • usethis::use_test(), which, if you use RStudio, will automatically create and open a test file corresponding to the R file in the right location. (If you’ve written the test file first, you can instead use usethis::use_r())

  • devtools::test_coverage_file(): again, if you use RStudio, this will look at the active file, run just the tests for that file, and report the coverage results. This is a great way to rapidly iterate to ensure that you have tested all the branches of new code.

New expectations

library(testthat)

This version of testthat introduces two important new expectations:

  • expect_invisible() makes it easier to check if a function returns its results invisibly. This is useful if you are testing a function that is called primarily for its side-effects, which should (as a general rule) invisibly return its first argument.

    greet <- function(name) {
      cat("Hello ", name, "!\n", sep = "")
      invisible(name)
    }
        
    x <- expect_invisible(greet("Hadley"))
    #> Hello Hadley!
    expect_equal(x, "Hadley")
    

    For symmetry, expect_visible() is also available, but you would not generally test for it, as visible return values are the default. Only use it if you find a bug related to visibilty and want to programmatically verify that it’s fixed.

  • New expect_mapequal(x, y) checks that x and y have the same names, and the same value associated with each name (i.e. it compares the values of the vector standardising the order of the names).

    exp <- list(a = 1, b = 2)
    expect_mapequal(list(a = 1, b = 2), exp)
    expect_mapequal(list(b = 2, a = 1), exp)
        
    expect_mapequal(list(b = 2), exp)
    #> Error: Names absent from `object`: "a",
    expect_mapequal(list(a = 3, b = 2), exp)
    #> Error: act$val[exp_nms] not equal to exp$val.
    #> Component "a": Mean relative difference: 0.6666667
    expect_mapequal(list(a = 1, b = 2, c = 3), exp)
    #> Error: Names absent from `expected`: "c",
    

    expect_mapequal() is related to expect_setequal(), which compares values, ignoring order and count:

    expect_setequal(c("a", "b"), c("b", "a"))
    expect_setequal(c("a", "b"), c("a", "a", "b"))
    

Acknowledgements

A big thanks to all 51 people who helped contribute to this release by reporting bugs, suggesting new features, or creating pull requests: @aabor, @AEBilgrau, @antaldaniel, @bflammers, @billdenney, @BillDunlap, @Bisaloo, @burchill, @chasemc, @colearendt, @comicfans, @dmenne, @euklid321, @flying-sheep, @gaborcsardi, @gabrielodom, @gvwilson, @hadley, @harvey131, @Hong-Revo, @HughParsonage, @jackhannah95, @jackwasey, @jarodmeng, @jennybc, @jsilve24, @kevinushey, @kforner, @lionel-, @maelle, @markvanderloo, @masaha03, @mb706, @mbjoseph, @merliseclyde, @mikejiang, @Mooskey, @olsgaard, @patr1ckm, @ramiromagno, @randy3k, @renkun-ken, @smbache, @stevecondylios, @topepo, @tramontini, @wch, @wsherwin, @Yuri-M-Dias, @yutannihilation, and @zappingseb.