R包开发:devtools::test()通过但devtools::check()测试失败排查
问题现象
R包开发过程中,交互式执行devtools::test()时57个测试全部通过,但运行devtools::check()时出现1个测试失败,报错输出如下:
-- Failure (test-plot_admissions_discharges_day_of_week.R:53:3): Admission numbers by day of the Week for improvised data -- `result` not equal to `correct_answers`. Component "Value": Mean absolute difference: 0.7941176 [ FAIL 1 | WARN 4 | SKIP 0 | PASS 56 ] Error: Test failures Execution halted
测试逻辑
失败的测试核心逻辑为比对plot_admissions_discharges_day_of_week函数返回的统计结果与预设的正确值,测试代码如下:
test_that("Admission numbers by day of the Week for improvised data", { # 预设正确结果 correct_answers <- tibble::tibble( Weekday = as.character(c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")), Event = c( "Avg_admissions", "Avg_admissions", "Avg_admissions", "Avg_admissions", "Avg_admissions", "Avg_admissions", "Avg_admissions", "Avg_discharges", "Avg_discharges", "Avg_discharges", "Avg_discharges", "Avg_discharges", "Avg_discharges", "Avg_discharges", "Non_emergency_admissions", "Non_emergency_admissions", "Non_emergency_admissions", "Non_emergency_admissions", "Non_emergency_admissions", "Non_emergency_admissions", "Non_emergency_admissions" ), Value = c(0.0, 0.5, 0.5, 0.5, 0.5, 0.5, 1.0, 2.0, 0.0, 1.5, 1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 0.5, 0.5, 0.5, 0.0, 0.5) ) correct_answers <- correct_answers %>% dplyr::arrange(Weekday, Event) # 构造测试输入数据 adm <- c("2018-12-09 09:30:00", "2018-12-09 09:30:00", "2018-12-09 09:30:00", "2018-12-11 09:30:00", "2018-12-09 09:30:00", "2018-12-12 09:30:00", "2018-12-09 09:30:00", "2018-12-13 08:00:00", "2018-12-09 09:30:00", "2018-12-14 08:00:00", "2018-12-09 09:30:00", "2018-12-09 09:30:00", "2018-12-15 08:00:00", "2018-12-16 08:00:00", "2018-12-16 08:00:00") disch <- c("2018-12-12 09:30:00", "2018-12-10 09:30:00", "2018-12-12 09:30:00", "2018-12-12 09:30:00", "2018-12-13 09:30:00", "2018-12-13 09:30:00", "2018-12-14 08:00:00", "2018-12-14 08:00:00", "2018-12-15 08:00:00", "2018-12-15 08:00:00", "2018-12-16 09:30:00", "2018-12-16 09:30:00", "2018-12-17 08:00:00", "2018-12-17 08:00:00", "2018-12-17 09:00:00") spell_class_col <- c( "ed_admission", "direct_admission", "ed_admission", "direct_admission", "ed_admission", "direct_comp_admission", "ed_admission", "direct_admission", "ed_admission", "direct_admission", "ed_admission", "direct_comp_admission", "ed_admission", "ed_admission", "direct_admission" ) spell_start <- as.POSIXct(adm, tz = "Europe/London") spell_end <- as.POSIXct(disch, tz = "Europe/London") test_avg_adm_disch <- tibble::tibble( spell_number = 101:115, spell_start, spell_end, spell_class_col ) # 调用目标函数 result <- plot_admissions_discharges_day_of_week( data = test_avg_adm_disch, startDate = as.Date("2018-12-10", tz = "Europe/London"), endDate = as.Date("2018-12-23", tz = "Europe/London"), returnPlot = FALSE, hospitalName = "{hospital_name}" ) result$Weekday <- as.character(result$Weekday) result <- result %>% dplyr::arrange(Weekday, Event) # 校验结果 expect_equal(tibble::as_tibble(result), tibble::as_tibble(correct_answers), tolerance = 0.01) })
手动逐行执行测试代码时,identical()校验返回两个数据框完全一致,差异仅存在于Value列,手动运行结果对比如下:
已排查方向
- 测试文件资源拷贝不全问题
- RStudio交互会话与R CMD check环境的区域设置差异
- 上述方向均未定位到问题根因
- 当前运行环境为R 4.1.0、Windows操作系统,包依赖配置如下:
Depends: R (>= 2.10) Imports: DescTools (>= 0.99.40), dplyr (>= 1.0.4), forcats (>= 0.5.1), ggplot2 (>= 3.3.3), ggpubr (>= 0.4.0), gtools (>= 3.9.2), lazyeval (>= 0.2.2), lubridate (>= 1.7.9.2), magrittr (>= 2.0.1), naniar (>= 0.6.0), padr (>= 0.6.0), purrr (>= 0.3.4), qicharts2 (>= 0.7.1), readr (>= 1.4.0), reshape2 (>= 1.4.4), rlang (>= 0.4.10), scales (>= 1.1.1), stringr (>= 1.4.0), tibble (>= 3.0.6), tidyr (>= 1.1.2) Suggests: knitr (>= 1.31), rmarkdown (>= 2.12), testthat (>= 3.0.2), vdiffr (>= 0.3.3) VignetteBuilder: knitr Encoding: UTF-8 LazyData: true RoxygenNote: 7.1.1
根因分析
问题核心是星期计算规则的环境依赖:
R CMD check运行时默认使用C本地化环境,周首日设置为周日,而本地交互式会话通常使用系统区域设置,周首日为周一。测试用例中2018-12-09为周日,若代码中提取星期、按周分组时未显式指定周首日规则,check环境下会将部分日期归到错误的星期分组,最终统计的日均入院/出院数值出现固定偏差,与报错中0.794的平均绝对差值完全吻合。
此外若使用基础函数weekdays()提取星期名称,C环境下返回的英文缩写和本地环境返回值可能存在格式差异,也会导致分组统计错误。
修复方案
- 函数逻辑层消除环境依赖
所有涉及星期计算的代码,全部显式指定规则,禁止依赖系统默认设置:- 替换基础函数
weekdays()为lubridate::wday(),调用时固定参数week_start = 1(指定周一为周首日),若需要返回星期缩写,增加参数label = TRUE, abbr = TRUE - 涉及按星期排序的逻辑,显式指定星期因子水平顺序为
c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"),避免按字符排序或默认因子顺序导致的错位
- 替换基础函数
- 测试层固定环境配置
在测试代码开头、或测试用例内显式设置时间区域配置,执行完成后恢复原有配置,避免环境差异影响:# 测试开头保存原有配置,设置为统一的C环境 old_locale <- Sys.getlocale("LC_TIME") Sys.setlocale("LC_TIME", "C") # 测试结束后恢复原有配置 on.exit(Sys.setlocale("LC_TIME", old_locale), add = TRUE) - 验证修复:重新执行
devtools::check(),确认测试全部通过。
内容的提问来源于stack exchange,提问作者Martynas Venckus
相关产品推荐
相关产品推荐

