Reputation:
I want to make informative stopifnot()
errors.
I've read: http://r-pkgs.had.co.nz/tests.html (the section at the end on using NSE to make informative test error print out for the example seems relevant) and http://adv-r.had.co.nz/Computing-on-the-language.html but I cant get this to print an informative error in concise code:
e <- new.env()
e$label <- c(1,2,3)
check_in_y <- function(x, z, e) {
stopifnot(eval(bquote(.(x) %in% e[[.(z)]])))
}
check_in_y(5,"label", e)
The output gives this (not so informative)
Error: eval(bquote(.(x) %in% e[[.(z)]])) is not TRUE
I want the error to be more informative, saying this:
Error: 5 %in% e[["label"]] is not TRUE
How can I get this to work? Or what's the best approach to achieve what I want
I know I could write an if condition not true then print my own error as an alternative, but the extra code is a hassle. I'd like to understand how to get NSE to get this to work.
Edit: My motivation from this approach came from reading hadley's comments (at http://r-pkgs.had.co.nz/tests.html):
However, if the expectation fails this doesn’t give very informative output:
expect_floor_equal("year", "2008-01-01 00:00:00") ## Error: floor_date(base, unit) not equal to as.POSIXct(time, tz = "UTC") ## Mean absolute difference: 31622400
Instead you can use a little non-standard evaluation to produce something more informative. The key is to use bquote() and eval(). In the bquote() call below, note the use of .(x) - the contents of () will be inserted into the call.
expect_floor_equal <- function(unit, time) { as_time <- function(x) as.POSIXct(x, tz = "UTC") eval(bquote(expect_equal(floor_date(base, .(unit)), as_time(.(time))))) } expect_floor_equal("year", "2008-01-01 00:00:00") ## Error: floor_date(base, "year") not equal to as_time("2008-01-01 00:00:00")
Upvotes: 3
Views: 1088
Reputation: 42544
There are a number of packages on CRAN which address the issue of meaningful error messages. I have started with the assertthat
and assertive
packages but I'm now using checkmate
for production code, especially for checking arguments to functions. BTW, checkmate
also extends Hadley's testthat
package.
With checkmate
,
checkmate::assert_choice(5, e[["label"]])
as well as
checkmate::assert_choice(5, e$label)
return the error message:
Error: Assertion on '5' failed: Must be element of set {'1','2','3'}, but is '5'.
It can also be used in the function
check_in_y <- function(x, z, e) {
checkmate::assert_choice(x, e[[z]])
}
check_in_y(5, "label", e)
which returns the error message:
Error in check_in_y(5, "label", e) :
Assertion on 'x' failed: Must be element of set {'1','2','3'}, but is '5'.
Upvotes: 4
Reputation: 99331
stopifnot
is just a convenience function for
if(!all(condition)) stop(standard message)
For custom messages, just write the code. You can replace the stopifnot
call with two lines:
check_in_y <- function(x, z, e) {
b <- bquote(.(x) %in% e[[.(z)]])
if(!eval(b)) stop(deparse(b), " is not TRUE", call. = FALSE)
}
check_in_y(5, "label", e)
# Error: 5 %in% e[["label"]] is not TRUE
Upvotes: 6