在R包中与用户进行测试交互

7

我正在开发一个R包,其中一个函数通过标准输入使用readline与用户交互。现在我想知道如何测试此功能的行为,最好使用testthat库。

似乎test_that函数假定用户输入的答案为""。我希望能够测试各种可能输入的条件下该功能的行为。

以下是一个小例子代码。在实际开发中,marryme函数定义在一个单独的文件中,并导出到命名空间中。 devtools::test()在最后一行上产生错误,因为回答从未变成“yes”。我想测试当用户键入"y"时该函数是否正确返回true。

library(testthat)

test_that("input", {
  marryme <- function() {
    ans <- readline("will you marry me? (y/n) > ")
    return(ans == "y")
  }

  expect_false(marryme())  # this is good
  expect_true(marryme())   # this is no good
})

marryme 拆分为两个函数。将除了 readline 以外的所有内容放在一个可以测试的函数中,并使用包含 readline 的包装函数调用该函数。顺便说一句,我不喜欢使用 readline 进行用户输入。 - Roland
2
谢谢,@Roland。你有什么替代readline的建议吗? - Kota Mori
1个回答

8

使用自定义连接进行readLines()

通过使用readLines()代替readline(),您可以定义连接,从而允许您使用全局选项对其进行自定义。

需要完成两个步骤:

  1. set a default option in your package in zzz.R that points to stdin:

    .onAttach <- function(libname, pkgname){
      options(mypkg.connection = stdin())
    }
    
  2. In your function, change readline to readLines(n = 1) and set the connection in readLines() to getOption("mypkg.connection")

示例

根据您提供的最小工作示例:


    library(testthat)

    options(mypkg.connection = stdin())

    marryme <- function() {
      cat("will you marry me? (y/n) > ")
      ans <- readLines(con = getOption("mypkg.connection"), n = 1)
      cat("\n")
      return(ans == "y")
    }

    test_that("input", {

      f <- file()
      options(mypkg.connection = f)
      ans <- paste(c("n", "y"), collapse = "\n") # set this to the number of tests you want to run
      write(ans, f)

      expect_false(marryme())  # this is good
      expect_true(marryme())   # this is no good
      # reset connection
      options(mypkg.connection = stdin())
      # close the file
      close(f)
    })
#> will you marry me? (y/n) > 
#> will you marry me? (y/n) >

这对于单个提示看起来不错,但我并没有立即看到如何在不必为每个输入行设置一系列选项的情况下扩展它以涵盖多个提示... - MichaelChirico
@MichaelChirico,无论有多少输入行,我每个测试套件只使用两个options()调用。挑战在于跟踪答案堆栈(在ans变量中)。 - ZNK
我遇到了这个错误。Error in nsenv [[f_name]](dirname(ns_path), package) : unbenutzte Argumente (dirname(ns_path), package) Ruft auf: suppressPackageStartupMessages ... <Anonymous> -> load_code -> <Anonymous> -> run_pkg_hook - SeGa
我在 .onAttach 方法中添加了 libname, pkgname。否则会抛出评论中提到的错误。 - SeGa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接