收集在 testthat::test_dir 期间在 R 中触发的 uncaught/untested 警告

Gather uncaught/untested warnings that were fired during testthat::test_dir in R

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

但是在测试过程中似乎没有办法获取或捕获警告运行!我知道测试是在封闭的环境中执行的,但是真的没有办法让 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
```

似乎 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)

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

例如在示例所示的测试用例中,它将显示三个结果字段,而只有两个 'expect_...' 语句。中间的将是 class 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' 将格式化 table 并且可以从警告对象中获取更多信息,如堆栈等。

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

更高级的示例:

这包括它发生的行号,另外还包括错误和跳过。它还显示 'file' 而不是 'test' (名称)。

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)