开发R包时如何使用testthat屏蔽预期的消息与警告
这问题我之前也踩过坑!根源在于你的测试代码里,每个expect_*函数都单独调用了一次foo()——三次调用就会三次触发message和warning。而expect_equal不会处理任何输出,expect_message只捕获message却漏了warning,expect_warning则漏了message,这些没被捕获的输出就会跑到测试报告里,造成干扰。
下面给你几个靠谱的解决方案,按推荐程度排序:
方案1:用evaluate_promise一次性搞定所有验证
这个方法只需要调用一次foo(),就能同时捕获返回值、message和warning,代码简洁还高效,是最推荐的做法。
修改后的测试代码:
test_that("foo works", { # 一次性捕获函数执行的所有信息 eval_result <- evaluate_promise(foo()) # 验证返回值 expect_equal(eval_result$result, "This is a result") # 验证message内容 expect_match(eval_result$messages, "This is a message") # 验证warning内容 expect_match(eval_result$warnings, "This is a warning") })
evaluate_promise会返回一个列表,里面包含了函数的返回值、所有输出的message和warning,这样所有预期的输出都会被捕获,不会泄露到测试报告里。
方案2:嵌套expect_message和expect_warning
如果你不想用evaluate_promise,可以把函数调用嵌套在两个expect函数里,这样能依次捕获message和warning,同时拿到返回值。
代码示例:
test_that("foo works", { # 先捕获message,再捕获warning,同时获取函数返回值 func_output <- expect_warning( expect_message(foo(), "This is a message"), "This is a warning" ) # 验证返回值 expect_equal(func_output, "This is a result") })
这里内层的expect_message会捕获并验证message,外层的expect_warning捕获并验证warning,所有输出都被处理了,自然不会出现在测试报告里。
方案3:用capture_messages和capture_warnings包裹执行
这种方式是用testthat的捕获函数把foo()的执行包裹起来,手动捕获所有输出,再分别验证:
test_that("foo works", { # 嵌套捕获message和warning captured_msgs <- capture_messages({ captured_warns <- capture_warnings({ func_output <- foo() }) }) # 逐一验证 expect_equal(func_output, "This is a result") expect_match(captured_msgs, "This is a message") expect_match(captured_warns, "This is a warning") })
这个方法逻辑清晰,但代码相对繁琐一些,适合需要对捕获的输出做更多自定义处理的场景。
效果验证
不管用哪个方案,重新运行测试后,你会发现测试报告里不会再出现那些预期的message和warning,只会显示测试的通过状态,干净多了!
内容的提问来源于stack exchange,提问作者pietrodito

