Is there a way to have multiple for expections for an expect_that unit test? For instance, for a given expect_that() statement, I'd like to expect that the function f() gives a warning and also returns the number 10.
test_that("f works as expected", {
expect_warning(f())
expect_equal(f(), 10)
}
)
If I understand your context correctly, this should work. The test would fail and report if either or both of the expectations weren't met.
To run the function only once, you could try wrapping the function within the test_that:
test_that("f works as expected", {
a <- tryCatch(f(), warning=function(w) return(list(f(), w)))
expect_equal(a[[2]], "warning text")
expect_equal(a[[1]], 10)
rm(a)
}
)
I haven't tested this so I'm not sure if it'll work in your particular case, but I've used similar approaches with test_that in the past.
context("Checking blah")
test_that("blah works",{
f <- function(){warning("blah"); return(10)}
expect_warning(x <- f())
expect_equal(x, 10)
})
You can save the output while checking for the warning. After that check that the output is what you expect.
Related
I want to test in R if a function
returns the correct value
throws the correct warning during calculation
For that, I created a reproducible example.
There a two scripts, the first one (e.g. test-warning-and-result.R) works fine and without any errors:
library(testthat)
f <- function(x) {
if (x < 0) {
warning("*x* is already negative")
return(x)
}
-x
}
test_that("warning and result", {
x = f(-1)
expect_that(x, equals(-1))
expect_warning(f(-1), "already negative")
})
However, when I run the tests from an external script (e.g. run-test.R), it logically throws a warning at "x = f(-1)"
library(testthat)
test_dir(".")
Picture of test results
Since I know there will be a warning and am testing for it, I'm searching for a way to omit the warning within test_that() from the test report. Ideally, I would not have to run the function twice but in one test.
Any Ideas would be appreciated
Ok after having one night's sleep on it I found a simple sollution:
Don't store the functions result in a variable x. Nest the two tests into each other, with the expect_warning outside
Change from
test_that("warning and result", {
x = f(-1)
expect_that(x, equals(-1))
expect_warning(f(-1), "already negative")
})
to
test_that("warning and result", {
expect_warning(expect_that(f(-1), equals(-1)), "already negative")
})
Is there a good way to warn just once in R?
What I currently do is the usual
a_reason_to_warn_has_occured <- FALSE
lapply(data, function(data) {
result <- do_something(data)
if (warning_reason)
a_reason_to_warn_has_occured <- TRUE
result
})
if (a_reason_to_warn_has_occured)
warning("This was bad.")
Is there a way to do this with less clutter/boiler-plate code?
I'd really love something like
lapply(data, function(data) {
result <- do_something(data)
warn_once_if(warning_reason, "This was bad.")
result
})
but I'm not sure whether it is even possible to implement this in R.
General remarks
I think your solution is fine, and I would probably use that in production code. Nevertheless, if you are interested in another, cooler but possibly more fragile way of doing this, read on.
A solution using non-standard evaluation
It is certainly possible to create a function that takes an expression, and evaluates it, and takes care about warning only once for each reason. You could use it like this:
warn_once(
lapply(data, function(data) {
result <- doSomething(data)
warn_if_first(reason = "bad data argument", message = "This was bad.")
result
})
)
It is also possible to do it in the form you suggested, but it is tricky to set the scope in which you want only one warning. E.g. look at these two examples. The first one is your original code.
lapply(data, function(data) {
result <- doSomething(data)
warn_if_first(warningReason, "This was bad.")
result
})
This is easy. You want one warning per the outer lapply block. But if you have the following one:
lapply(data, function(data) {
result <- doSomething(data)
sapply(result, function(x) {
warn_if_first(warningReason, "This was bad.")
})
result
})
then (at least with the straightforward implementation of warn_if_first) you will get one warning per sapply call, and there is no easy way to tell warn_if_first if you want one warning per lapply call.
So I suggest the form above, that explicitly specifies the environment in which you will get a single warning.
Implementation
warn_once <- function(..., asis = FALSE) {
.warnings_seen <- character()
if (asis) {
exprs <- list(...)
} else {
exprs <- c(as.list(match.call(expand.dots = FALSE)$...))
}
sapply(exprs, eval, envir = parent.frame())
}
warn_if_first <- function(reason, ...) {
## Look for .warnings_seen
for (i in sys.nframe():0) {
warn_env <- parent.frame(i)
found_it <- exists(".warnings_seen", warn_env)
if (found_it) { break }
}
if (!found_it) { stop("'warn_if_first not inside 'warn_once'") }
## Warn if first, and mark the reason
.warnings_seen <- get(".warnings_seen", warn_env)
if (! reason %in% .warnings_seen) {
warning(...)
.warnings_seen <- c(.warnings_seen, reason)
assign(".warnings_seen", .warnings_seen, warn_env)
}
}
Example usage
Let's try it!
warn_once({
for (i in 1:10) { warn_if_first("foo", "oh, no! foo!") }
for (i in 1:10) { warn_if_first("bar", "oh, no! bar!") }
sapply(1:10, function(x) {
warn_if_first("foo", "oh, no! foo again! (not really)")
warn_if_first("foobar", "foobar, too!")
})
"DONE!"
})
Which outputs
[1] "DONE!"
Warning messages:
1: In warn_if_first("foo", "oh, no! foo!") : oh, no! foo!
2: In warn_if_first("bar", "oh, no! bar!") : oh, no! bar!
3: In warn_if_first("foobar", "foobar, too!") : foobar, too!
and this seems about right. A glitch is that the warning is coming warn_if_first, and not from its calling environment, as it should be, but I have no idea how to fix this. warning also uses non-standard evaluation, so it is not as simple as just doing eval(warning(...), envir = parent.frame()). You can supply call. = FALSE to warning() or to warn_if_first(), and then you will get
[1] "DONE!"
Warning messages:
1: oh, no! foo!
2: oh, no! bar!
3: foobar, too!
which is probably better.
Caution
While I don't see any obvious problems with this implementation, I cannot guarantee that it does not break in some special circumstances. It is very easy to make mistakes with non-standard evaluation. Some base R functions, and also some popular packages like magrittr, also use non-standard evaluation, and then you have to be doubly cautious, because there might be interactions between them.
The variable name I used for the book-keeping, .warnings_seen is special enough, so that it will not interfere with other code most of the time. If you want to be (almost) completely sure, generate a long random string and use that as the variable name instead.
Further reading about scoping
The section about scoping in "An introduction to R": http://cran.r-project.org/doc/manuals/R-intro.html#Scope
The chapter about non-standard evaluation in Hadley Wickam's book: http://cran.r-project.org/doc/manuals/R-intro.html#Scope, especially the part about dynamic scoping.
Based on the comments and Gabors answer, here is the result of me trying to implement a non-scoping solution. It is based on comparing the tracebacks of the calls to warn_once. Take care as this is just a quick draft and definitively not perfect. For more information, see below.
warn_once <- function(mesg) {
trace <- traceback(0)
if (exists(".warnings_shown", sys.frame(1))) {
warn_list <- get(".warnings_shown", sys.frame(1))
found_match <- FALSE
for (warn in warn_list)
if (all(unlist(Map(`==`, warn, trace))))
return()
warn_list[[length(warn_list)+1]] <- trace
assign(".warnings_shown", warn_list, envir=sys.frame(1))
warning(mesg)
} else {
assign(".warnings_shown", list(trace), envir=sys.frame(1))
warning(mesg)
}
}
As a test case, I used ...
func <- function(x) {
func2(x)
func2(not(x))
func2(x)
func2(not(x))
}
func2 <- function(x) {
if(x) for(i in 1:3) warn_once("yeah")
if(not(x)) warn_once("nope")
warn_once("yeah")
}
func(T)
... which resulted in ...
Warning in warn_once("yeah") : yeah
Warning in warn_once("yeah") : yeah
Warning in warn_once("nope") : nope
Warning in warn_once("yeah") : yeah
Warning in warn_once("yeah") : yeah
Warning in warn_once("yeah") : yeah
Warning in warn_once("nope") : nope
Warning in warn_once("yeah") : yeah
... and a lot of clutter output from the call to traceback.
Notes:
I guess it is somehow possible to suppress the output of the calls to traceback(), but I wasn't able to do so.
This identifies warnings based on their position in the frame stack, as opposed to identifying them by their warning message, as in Gabors answer. This can be but isn't necessarily desired behavior.
From the traceback, one could probably infer the name of the calling function and add it to the warning message, which might be useful.
Obviously, an optional parameter could be introduced for specifying the number of levels to go up in the frame stack in searching for ".warnings_shown".
Comments and improvements (just edit!) most welcome.
I am working on a code and ran into a problem I can't seem to fix. Basically the idea is that I have one overall function and within that function are several other functions. My problem is: within one of those function I need the option to stop if a certain condition is not satisfied, but using stopifnot() of if (...) {stop} stops the overall functions as well. Is there a way in which I can stop the specific function from running without also stopping the overall function?
For example:
full=function(){
message("before")
x=2
small=function(x){
if (x<3){stop("smaller than 3")
print(x)
}
}
small(x)
message("after")
}
full()
What I want to do here is quit the "small" function if x is smaller than 3 (so x is not printed), but I still want to run the message "after". Is there a way to do that?
Thanks in advance.
Perhaps you are just looking for the return() function. Otherwise,
try error handling facilities:
full <- function() {
message("before")
small <- function(x){
if (x<3) {
# or just e.g. return(NULL)
stop("smaller than 3")
}
print(x)
}
tryCatch({
small(2)
}, error=function(err) {
# do nothing on error
})
message("after")
}
full()
## before
## after
Is there any way to stop an R program without error?
For example I have a big source, defining several functions and after it there are some calls to the functions. It happens that I edit some function, and want the function definitions to be updated in R environment, but they are not actually called.
I defined a variable justUpdate and when it is TRUE want to stop the program just after function definitions.
ReadInput <- function(...) ...
Analyze <- function(...) ...
WriteOutput <- function(...) ...
if (justUpdate)
stop()
# main body
x <- ReadInput()
y <- Analyze(x)
WriteOutput(y)
I have called stop() function, but the problem is that it prints an error message.
ctrl+c is another option, but I want to stop the source in specific line.
The problem with q() or quit() is that it terminates R session, but I would like to have the R session still open.
As #JoshuaUlrich proposed browser() can be another option, but still not perfect, because the source terminates in a new environment (i.e. the R prompt will change to Browser[1]> rather than >). Still we can press Q to quit it, but I am looking for the straightforward way.
Another option is to use if (! justUpdate) { main body } but it's clearing the problem, not solving it.
Is there any better option?
I found a rather neat solution here. The trick is to turn off all error messages just before calling stop(). The function on.exit() is used to make sure that error messages are turned on again afterwards. The function looks like this:
stop_quietly <- function() {
opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
stop()
}
The first line turns off error messages and stores the old setting to the variable opt. After this line, any error that occurs will not output a message and therfore, also stop() will not cause any message to be printed.
According to the R help,
on.exit records the expression given as its argument as needing to be executed when the current function exits.
The current function is stop_quietly() and it exits when stop() is called. So the last thing that the program does is call options(opt) which will set show.error.messages to the value it had, before stop_quietly() was called (presumably, but not necessarily, TRUE).
There is a nice solution in a mailing list here that defines a stopQuietly function that basically hides the error shown from the stop function:
stopQuietly <- function(...) {
blankMsg <- sprintf("\r%s\r", paste(rep(" ", getOption("width")-1L), collapse=" "));
stop(simpleError(blankMsg));
} # stopQuietly()
> stopQuietly()
I have a similar problem and, based on #VangelisTasoulas answer, I got a simple solution.
Inside functions, I have to check if DB is updated. If it is not, stop the execution.
r=readline(prompt="Is DB updated?(y/n)")
Is DB updated?(y/n)n
if(r != 'y') stop('\r Update DB')
Update DB
Just putting \r in the beginning of the message, overwrite Error: in the message.
You're looking for the function browser.
You can use the following solution to stop an R program without error:
if (justUpdate)
return(cat(".. Your Message .. "))
Just return something at the line you want to quit the function:
f <- function(x, dry=F) {
message("hi1")
if (dry) return(x)
message("hi2")
x <- 2*x
}
y1 <- f(2) # = 4 hi1 hi2
y2 <- f(2, dry=T) # = 2 hi1
In addition to answer from Stibu on Mar 22 '17 at 7:29, if you want to write a message as a part of stop(), this message is not written.
I perceive strange that following two lines have to be used meaning on.exit(options(options(show....))) doesn't work.
opt <- options(show.error.messages = F)
on.exit(options(opt))
I had forgotten the answer to this and needed to look it up and landed here... You posted the hint to the answer in your question...
ctrl+c is another option, but I want to stop the source in specific line.
Signal an error, warning, or message
rlang::inform("Updated Only")
rlang::interrupt()
I've found it good to write a script and run it with source(). In the script, a write exit statements as a special class of error that a tryCatch() can pick up and send back as just a message:
exit <- function(..., .cl = NULL) {
# Use to capture acceptable stop
cond <- structure(
list(.makeMessage(...), .cl),
class = c("exitError", "error", "condition"),
names = c("message", "call")
)
stop(cond)
}
foo <- function() {
exit("quit here")
1
}
tryCatch(
# rather than foo(), you might use source(filename)
foo(),
exitError = function(e) message(e$message)
)
#> quit here
Created on 2022-01-24 by the reprex package (v2.0.1)
You can use with_options() in the withr package to temporarily disable error messages and then you can call stop() directly.
Here is an example:
weird_math <- function(x, y, z) {
if (x > z) {
withr::with_options(
list(show.error.messages = FALSE),
{
print("You can execute other code here if you want")
stop()
}
)
}
# only runs if x <= z
x + y ^ z
}
weird_math(1, 2, 3)
[1] 9
weird_math(3, 2, 1)
[1] "You can execute other code here if you want"
why not just use an if () {} else {}? It's only a couple of characters...
f1 <- function(){}
f2 <- function(){}
if (justUpdate) {
} else {
# main body
}
or even
f1 <- function(){}
f2 <- function(){}
if (!justUpdate) {
# main body
}
The below code work for me stopped without error messages.
opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
break
Is there any way to stop an R program without error?
For example I have a big source, defining several functions and after it there are some calls to the functions. It happens that I edit some function, and want the function definitions to be updated in R environment, but they are not actually called.
I defined a variable justUpdate and when it is TRUE want to stop the program just after function definitions.
ReadInput <- function(...) ...
Analyze <- function(...) ...
WriteOutput <- function(...) ...
if (justUpdate)
stop()
# main body
x <- ReadInput()
y <- Analyze(x)
WriteOutput(y)
I have called stop() function, but the problem is that it prints an error message.
ctrl+c is another option, but I want to stop the source in specific line.
The problem with q() or quit() is that it terminates R session, but I would like to have the R session still open.
As #JoshuaUlrich proposed browser() can be another option, but still not perfect, because the source terminates in a new environment (i.e. the R prompt will change to Browser[1]> rather than >). Still we can press Q to quit it, but I am looking for the straightforward way.
Another option is to use if (! justUpdate) { main body } but it's clearing the problem, not solving it.
Is there any better option?
I found a rather neat solution here. The trick is to turn off all error messages just before calling stop(). The function on.exit() is used to make sure that error messages are turned on again afterwards. The function looks like this:
stop_quietly <- function() {
opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
stop()
}
The first line turns off error messages and stores the old setting to the variable opt. After this line, any error that occurs will not output a message and therfore, also stop() will not cause any message to be printed.
According to the R help,
on.exit records the expression given as its argument as needing to be executed when the current function exits.
The current function is stop_quietly() and it exits when stop() is called. So the last thing that the program does is call options(opt) which will set show.error.messages to the value it had, before stop_quietly() was called (presumably, but not necessarily, TRUE).
There is a nice solution in a mailing list here that defines a stopQuietly function that basically hides the error shown from the stop function:
stopQuietly <- function(...) {
blankMsg <- sprintf("\r%s\r", paste(rep(" ", getOption("width")-1L), collapse=" "));
stop(simpleError(blankMsg));
} # stopQuietly()
> stopQuietly()
I have a similar problem and, based on #VangelisTasoulas answer, I got a simple solution.
Inside functions, I have to check if DB is updated. If it is not, stop the execution.
r=readline(prompt="Is DB updated?(y/n)")
Is DB updated?(y/n)n
if(r != 'y') stop('\r Update DB')
Update DB
Just putting \r in the beginning of the message, overwrite Error: in the message.
You're looking for the function browser.
You can use the following solution to stop an R program without error:
if (justUpdate)
return(cat(".. Your Message .. "))
Just return something at the line you want to quit the function:
f <- function(x, dry=F) {
message("hi1")
if (dry) return(x)
message("hi2")
x <- 2*x
}
y1 <- f(2) # = 4 hi1 hi2
y2 <- f(2, dry=T) # = 2 hi1
In addition to answer from Stibu on Mar 22 '17 at 7:29, if you want to write a message as a part of stop(), this message is not written.
I perceive strange that following two lines have to be used meaning on.exit(options(options(show....))) doesn't work.
opt <- options(show.error.messages = F)
on.exit(options(opt))
I had forgotten the answer to this and needed to look it up and landed here... You posted the hint to the answer in your question...
ctrl+c is another option, but I want to stop the source in specific line.
Signal an error, warning, or message
rlang::inform("Updated Only")
rlang::interrupt()
I've found it good to write a script and run it with source(). In the script, a write exit statements as a special class of error that a tryCatch() can pick up and send back as just a message:
exit <- function(..., .cl = NULL) {
# Use to capture acceptable stop
cond <- structure(
list(.makeMessage(...), .cl),
class = c("exitError", "error", "condition"),
names = c("message", "call")
)
stop(cond)
}
foo <- function() {
exit("quit here")
1
}
tryCatch(
# rather than foo(), you might use source(filename)
foo(),
exitError = function(e) message(e$message)
)
#> quit here
Created on 2022-01-24 by the reprex package (v2.0.1)
You can use with_options() in the withr package to temporarily disable error messages and then you can call stop() directly.
Here is an example:
weird_math <- function(x, y, z) {
if (x > z) {
withr::with_options(
list(show.error.messages = FALSE),
{
print("You can execute other code here if you want")
stop()
}
)
}
# only runs if x <= z
x + y ^ z
}
weird_math(1, 2, 3)
[1] 9
weird_math(3, 2, 1)
[1] "You can execute other code here if you want"
why not just use an if () {} else {}? It's only a couple of characters...
f1 <- function(){}
f2 <- function(){}
if (justUpdate) {
} else {
# main body
}
or even
f1 <- function(){}
f2 <- function(){}
if (!justUpdate) {
# main body
}
The below code work for me stopped without error messages.
opt <- options(show.error.messages = FALSE)
on.exit(options(opt))
break