Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a package test in R to see if warning is correctly thrown?

I'm writing some tests for an R package and would like to have R CMD check verify that functions display the correct warnings for certain inputs. But I can't figure out how to capture the warning output so that I can test it.

So if I have a function like:

throwsWarning<-function(x){
  if(x>0){
    warning('Argument "x" is greater than zero, results may be incorrect')
  }
  # do something useful ...
}

I'd want a something in my test file like:

warningOutput <-try( throwsWarning(1))
if (warningOutput!='Argument "x" is greater than zero, results may be incorrect'){
  stop('function "throwsWarning" did not produce correct warning when x>0')
}

So far I've found possible partial solutions by changing options so that warnings are treated as errors and the surrounding with a trycatch block. Also considered testing value of last.warning, but that seems dangerous if the warning is not thrown (would test previous value). Seems like there must be an easy way to do this that I'm missing?

like image 430
skyebend Avatar asked Jan 23 '13 02:01

skyebend


People also ask

How do I test a package in R?

Assuming you're in a package directory, just run usethis::use_test("name") to create a test file, and set up all the other infrastructure you need. If you're using RStudio, press Cmd/Ctrl + Shift + T (or run devtools::test() if not) to run all the tests in a package.

How do you find the error code in R?

You can use traceback() to locate where the last error occurred. Usually it will point you to a call you make in your function. Then I typically put browser() at that point, run the function again and see what is going wrong. The number means how deep we are in the nested functions.

How do I turn off error messages in R?

8.3 Ignoring conditions The simplest way of handling conditions in R is to simply ignore them: Ignore errors with try() . Ignore warnings with suppressWarnings() . Ignore messages with suppressMessages() .

How to run all tests in a package in R?

If you’re using RStudio, press Cmd/Ctrl + Shift + T (or run devtools::test () if not) to run all the tests in a package.

How do I check coverage of a package in R?

It usually measures coverage as a ratio, e.g. 60% of all lines, functions, etc. If you want to check the coverage of a package, you simply have to: Say you have this R script (and its companion test): In the “Source” tab you will see a report that shows which lines have been tested and which have not.

How do I create a test file in R?

Assuming you’re in a package directory, just run usethis::use_test ("name") to create a test file, and set up all the other infrastructure you need. If you’re using RStudio, press Cmd/Ctrl + Shift + T (or run devtools::test () if not) to run all the tests in a package.

What is the best tool for unit testing in R?

testthat is the most popular unit testing package for R and is used by thousands of CRAN packages. If you’re not familiar with testthat, the testing chapter in R packages gives a good overview, along with workflow advice and concrete examples. The easiest way to get started is with usethis.


2 Answers

The testthat package has an expect_warning and gives_warning function that you can use.

From the examples, you would do something like this:

R> library(testthat)
R> expect_that(warning("this is a warning"), gives_warning("is a"))
## This does not raise an error, but:
R> expect_that(warning("this is a warning"), gives_warning("nope"))
Error: warning("this is a warning") does not match 'nope'. Actual value: 
this is a warning

So, gives_warning is regular expression that is matched against the warning that is supposed to be emitted. If the regex does not match (or no warning is thrown), then a red flag is raised.

Equally, using the shorter expect_warning:

R> expect_warning(warning("this is a warning"), "is a")
like image 177
Steve Lianoglou Avatar answered Nov 14 '22 23:11

Steve Lianoglou


If you're writing your own package, it might make sense to make use of R's condition system by throwing (and catching) particular types of errors or warnings. So

myFun <- function(x) {
    if (any(is.na(x))) {
        w <- simpleWarning("'x' contains NA values")
        class(w) <- c("HasNA", class(w))
        warning(w)
    }
    if (any(x < 0))
        warning("'x' contains values less than 0")
    x
}

and then in your test, e.g., with library(RUnit), use tryCatch and pick off just the conditions that you're interested in testing, i.e., warnings with class HasNA:

test_myFun_NAwarning <- function() {
    warnOccurred <- FALSE
    tryCatch(myFun(1:5), HasNA = function(w) warnOcccurred <<- TRUE)
    checkTrue(!warnOccurred)
    tryCatch(myFun(-(1:5)), HasNA = function(w) warnOcccurred <<- TRUE)
    checkTrue(!warnOccurred)
    tryCatch(myFun(c(1:5, NA)), HasNA = function(w) warnOccurred <<- TRUE)
    checkTrue(warnOccurred)
}

leading to

> test_myFun_NAwarning()
[1] TRUE
Warning message:
In myFun(-(1:5)) : 'x' contains values less than 0

which shows that tryCatch is catching just the particular warning you're interested in, and doing so in a way that does not rely on matching the text of the string. Maybe you'd have a helper function .warn to make all warnings for your package. See ?withCallingHandlers for additional detail; withCallingHandlers and muffleRestart are how one might cope with continuing evaluation after a warning occurred, rather than stopping the way tryCatch does.

like image 21
Martin Morgan Avatar answered Nov 14 '22 23:11

Martin Morgan