如何为R包创建支持动态输入的函数并适配roxygen2示例与testthat测试
1 Roxygen2示例兼容方案
核心原因是readline()仅在交互式R会话中生效,非交互式场景(如R CMD check、CI运行示例)会返回空值导致报错。解决方法分为两步:
- 函数内增加交互式环境判断,非交互式场景给定合理默认值,同时预留直传参数跳过交互
修改后的完整函数逻辑:
#' Dynamic Input Example #' #' @param input A number #' @param dynamic_input 可选,直接传入动态值跳过交互,默认NULL #' @return Console input times 2 minus 1 #' @export #' #' @examples #' \dontrun{ #' # 交互式场景走弹窗输入 #' exampler(4) #' } #' # 非交互式场景/提前知晓输入值时直接传参,可正常跑通检查 #' exampler(4, dynamic_input = 10) exampler <- function(input, dynamic_input = NULL){ if (is.null(dynamic_input)) { dynamic_input <- if (interactive()) { as.numeric(readline(prompt=paste("Give a value between", input*3-1, "and", input*7+2))) } else { # 非交互式默认取区间中间值,可根据业务需求调整 input * 5 } } return(dynamic_input*2-1) }
- 示例中需要人工交互的部分用
\dontrun{}包裹,同时补充可自动运行的传参示例,即可通过R CMD check的示例检查。
2 testthat测试方案
核心思路是mock掉readline()函数的返回值,不需要真的等待用户输入,测试代码示例:
library(testthat) test_that("exampler计算逻辑符合预期", { # 方法1:使用testthat内置mock功能(testthat >=3.0.0支持) local_mocked_bindings( readline = function(...) "12", .package = "base" ) expect_equal(exampler(4), 12*2 -1) # 预期返回23 # 方法2:使用mockery包的stub功能,兼容性更好 # mockery::stub(exampler, "readline", "8") # expect_equal(exampler(4), 15) })
如果有输入校验逻辑(比如判断用户输入是否在要求的区间内),也可以通过mock不同的输入值覆盖边界场景测试。
3 R包场景下的动态输入最优方案
- 基础交互场景:base包自带的
readline()已经足够使用,只要做好交互式/非交互式分支判断、预留直传参数即可,不需要引入额外依赖 - 复杂交互场景:
- 密码输入:使用
getPass::getPass(),输入内容不会在控制台回显 - 菜单式多选:使用base包自带的
menu()函数,无需额外依赖 - 样式化交互/输入校验:使用
cli包的cli_ask_*系列函数,支持输入格式校验、自定义提示样式,是当前R包生态的主流选择
- 密码输入:使用
- 所有交互类函数都建议预留直传参数,方便用户在脚本、批处理场景调用,也能简化测试流程。
内容的提问来源于stack exchange,提问作者Kezrael
相关产品推荐
相关产品推荐

