Hide {cli} messages in tests with {testthat} - r

I use {cli} messages in one of my packages. I would like to hide these messages in my tests because they clutter the testthat results. Is there a way to do that?
I've seen that {cli} has a TESTTHAT environment variable but I don't know if it exists for this purpose, and I don't know how to use it. Note that I would prefer a solution that is simple to implement, such as a test global option. I don't want to manually edit all my tests or messages.
Reproducible example:
library(testthat)
library(cli)
test_that("addition works", {
cli_alert_info("This message should not appear")
expect_equal(1+1, 2)
})
#> i This message should not appear
#> Test passed
Edit: I could create a custom test_that() that would wrap suppressMessages() like the following:
my_test_that <- function(desc, code) {
test_that(desc, {
suppressMessages({
code
})
})
}
my_test_that("addition works", {
cli_alert_danger("This message should not appear")
expect_equal(1+1, 2)
})
#> Test passed
and then store it in tests/testthat/setup.R. The problem is that if a test fails, it indicates the line in my_test_that() and not the line where the error actually appears (so basically all errors and warnings will refer to the same line):
my_test_that("addition works", {
cli_alert_danger("This message should not appear")
expect_equal(1+4, 2)
})
-- Failure (.active-rstudio-document:6:5): addition works ----------------------
1 + 4 (`actual`) not equal to 2 (`expected`).
Here, the error refers to the line with suppressMessages(). This makes it much harder to find the source of the problem.

One solution was given in this Github issue. Using withr::with_options() or withr::local_options() with option cli.default_handler = function(...) { } seems to work.
library(testthat)
library(cli)
my_test_that <- function(desc, code) {
withr::with_options(
list(
cli.default_handler = function(...) { },
usethis.quiet = TRUE
),
test_that(desc, {
code
})
)
}
my_test_that("addition works", {
cli_alert_danger("This message should not appear")
expect_equal(1+2, 2)
})
-- Failure (.active-rstudio-document:15:3): addition works ---------------------
1 + 2 not equal to 2.
1/1 mismatches
[1] 3 - 2 == 1
Note that this will remove all cli messages so it is not possible to use expect_message() in my_test_that().

Related

R: How to omit tested warning message from test report when testing for result AND warning

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")
})

Why is helper file in testthat sourced twice

I run into trouble when using uuids in tests, as the helper file is sourced twice. Why does that happen? Is there a way to avoid second sourcing?
For a reproducible example, just create a new package, put a file called "helper-data.R" in /tests/testhat/ with th following content
if (!exists("test_ind")) {
test_ind <- 1
print(paste0("test_ind = ", test_ind))
test_ind <- test_ind + 1
} else {
print(paste0("test_ind = ", test_ind))
test_ind <- test_ind + 1
}
and create an file "test-1.R" in /tests/testhat/ with th following empty test
context("test1")
test_that("test1", {
# expect_equal(1, 1)
})
and you will see that test_ind is 2 in the end.
I found this link but I don't see how that could solve my problem.
Update: Created issue on github-testthat
I just recieved a message that this is solved in the dev-version on Github.

Multiple expectations with test_that

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.

Stopping an R script quietly and return control to the terminal [duplicate]

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

Stop an R program without error

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

Resources