【问题标题】:Gather uncaught/untested warnings that were fired during testthat::test_dir in R收集在 R 中的 testthat::test_dir 期间触发的未捕获/未经测试的警告
【发布时间】:2022-01-06 09:30:41
【问题描述】:

我想运行所有测试并获取测试结果并生成警告,以编程方式创建一个降价报告,显示测试结果和测试代码中出现的潜在警告。

但在测试运行期间似乎无法获取或捕获警告!我知道测试是在封闭的环境中执行的,但是真的没有办法让 testthat 向我提供抛出的警告吗?

在以下设置中,warn_list 变量始终为空。

最小示例的三个文件:

./tests/testthat.R

library(testthat)
 
warn_list <- list()
outcome <- withCallingHandlers(

    testthat::test_dir(testthat::test_path()),
   
    warning = function(w) {
        warn_list <<- c(warn_list, list(msg = w$message))
    }
)

rmarkdown::render(input = './tests/create_test_report.Rmd')

请注意,Rmd 文件中使用了outcome(和warn_list)变量。

./tests/testthat/test_thrown_warn.R

test_that("Throws Warning", {

    testthat::expect_equal(
        {
            warning('Example warning fired inside test!')  # WHERE WARN IS THROWN
            5
        }, 5)
   
})

./tests/create_test_report.Rmd

---
title: "test_results_overview"
output: md_document
---

## Produced warnings during the tests:
 
```{r warnings_during_testing, echo=FALSE}
knitr::kable(warn_list)                     # WHERE I TRY TO SHOW IT
```

【问题讨论】:

  • 我怀疑testthat 正在捕捉警告。也许将 reporter 参数设置为非默认(或自定义)的参数会得到你想要的。
  • 说得好,我读到了不同的(相当无证的)记者,但没有对它们进行试验。如果我没记错的话,测试返回的 testthat_results 类对象仍然保持不变......?会看看
  • 如果您对答案有疑问:如果您只想要警告(或跳过或失败)本身,请选择“user2554330”他的答案。如果您喜欢它发生的行以及更多上下文(例如文件/测试/上下文等),请选择我的答案。
  • 实际上,我的回答中提到的awarning 对象不仅仅是一个条件对象。 awarning$test 会给出测试的名称,位置可以从awarning$srcref 组件中找到。

标签: r warnings testthat


【解决方案1】:

SummaryReporter 记者对象似乎记录了警告。正如您在评论中提到的那样,这些记录很少,但这似乎可以做到:

library(testthat)
summary <- SummaryReporter$new()
test_file("somewarnings.R", reporter = summary)
summary$warnings$as_list()

每个警告都会在最后一条语句的列表中产生一个条目。它们被存储为条件对象,所以你可以做类似的事情

awarning <- summary$warnings$as_list()[[1]]
getSrcFilename(awarning$srcref)

【讨论】:

  • 这太棒了!我想出了一些替代方法,我也会发布,但我认为你的方法更好,所以会接受它作为答案。
【解决方案2】:

testthat_results 对象中实际上提供了替代解决方案。 它确实包括特定的测试用例响应,例如警告、跳过的测试或崩溃测试的错误,但内容结构不合理。

例如在示例中显示的测试用例中,它将显示三个结果字段,而只有两个 'expect_...' 语句。中间的将属于c('expectation_warning', 'expectation', 'condition') 类并包含警告对象。

另一种解决方案是从如下结果中获取这些警告(以及可选的错误和跳过作为奖励):

outcome <- testthat::test_dir(testthat::test_path(), stop_on_failure = FALSE)

create_warn_df <- function(res) {
    data.frame(test = res$test, warning_msg = res$message)
}
 
warn_df <- data.frame()
for (i_test in seq_along(outcome)) {
    tst <- outcome[[i_test]]
   
    for (i_res in seq_along(tst$results)) {
        res <- tst$results[[i_res]]
       
        if (is(res, "expectation_warning")) {
            warn_df <- rbind(warn_df, create_warn_df(res))
        } 
    }
}
 
knitr::kable(warn_df)

注意:'create_warning_str' 将格式化表格,并可以从警告对象中获取更多信息,如堆栈等。

测试结果位于outcome[[X]]$results[[Y]] 中,其中X 是正在处理的文件,Y 是测试用例(或警告,如前所述)。 Y 元素的数量不等于测试用例的数量。

一个更高级的例子:

这一项包括发生位置的行号,另外还包括错误和跳过。它还显示“文件”而不是“测试”(名称)。

create_warn_df <- function(file, src_ref, warn_msg) {
    data.frame('file' = file, 'line' = getSrcLocation(src_ref), 'warning_msg' = warn_msg)
}
 
warn_df <- data.frame()
for (i_test in seq_along(outcome)) {
    tst <- outcome[[i_test]]
   
    for (i_res in seq_along(tst$results)) {
        res <- tst$results[[i_res]]
       
        if (is(res, "expectation_warning")) {
            warn_df <- rbind(warn_df, create_warn_df(tst$file, res$srcref, res$message))
        } else if (is(res, "expectation_error")) {
            warn_df <- rbind(warn_df, create_warn_df(tst$file, res$srcref, paste('An error crashed this test:', res$message)))
        } else if (is(res, "expectation_skip")) {
            warn_df <- rbind(warn_df, create_warn_df(tst$file, res$srcref, paste('A test is skipped,', res$message)))
        }
    }
}
 
knitr::kable(warn_df)

【讨论】:

    猜你喜欢
    • 2016-06-13
    • 1970-01-01
    • 1970-01-01
    • 2018-04-27
    • 1970-01-01
    • 2020-12-25
    • 1970-01-01
    • 2022-07-15
    相关资源
    最近更新 更多