【问题标题】:How to test an R function which uses Sys.time()?如何测试使用 Sys.time() 的 R 函数?
【发布时间】:2018-08-22 00:40:20
【问题描述】:

我在一个软件包中有以下 R 函数,它将Sys.time() 输出到输出,以便用户了解算法是如何进行的:

func = function(num1, num2){
    result = num1 + num2
    return(paste0(' ', as.character(Sys.time()), ':  with result: ', result))
}

该函数的使用示例如下:

> func(2, 2)
[1] " 2018-03-11 07:24:05:  with result: 4"
> 

我需要测试这个功能。通常,我会使用 testthat 包:

https://cran.r-project.org/web/packages/testthat/index.html

问题:如何set Sys.time() 来测试这个功能?是否有其他方法可以对此进行测试?

如果没有Sys.time(),这个过程会很简单:

library(testthat)
expect_equal(func(2, 2), 4)
expect_equal(func(2, -2), 0)
expect_error(func('a', 'b'))

【问题讨论】:

  • 如何重写函数,使其返回包含结果和时间的类 {whateveryouwant} 的对象,并制作自定义打印函数类 {whateveryouwant}。然后在测试时可以忽略时间方面,直接得到结果。否则,您将对结果进行正则表达式,以后会很头疼。
  • @Dason “否则你将正则表达式的结果,这只会是一个头疼的事。”我不确定为什么。您可以在最后一个 : 之后进行正则表达式并检查 4 是否存在
  • 您刚刚回答了自己的问题。你不能也做这样的事情吗? expect_equal(func(2, 2), paste0(' ', as.character(Sys.time()), ': with result: ', 4)) 这样你也在测试你的函数是否正确返回了时间
  • @EB2127 然后,当他们决定要更改测试中断的格式时。将实际数据与它们的格式化方式分开是在这里进行体面测试的良好第一步。
  • 另外,如果有人想实际使用输出而不是在控制台中查看,那么以易于使用的格式返回输出会更有意义...

标签: r unit-testing testing testthat


【解决方案1】:

我认为您应该重写您的函数,以便它在列表中返回结果和时间。然后创建一个执行格式化的自定义打印函数。这样,您仍然可以直接以编程方式处理结果,而无需使用正则表达式来提取结果。这是一个例子。

func <- function(num1, num2){
  result <- num1 + num2
  time <- Sys.time()
  output <- list(result = result, time = time)
  class(output) <- "MyClass"
  return(output)
}

print.MyClass <- function(obj, ...){
  text <- paste0(' ', as.character(obj$time), ': with result: ', obj$result)
  print.default(text)
}

使用这个...

> func(2, 2)
[1] " 2018-03-13 13:26:22: with result: 4"
> o <- func(2,2)
> o$result
[1] 4
> o$time
[1] "2018-03-13 13:26:27 CDT"
> 
> expect_equal(func(2, 2)$result, 4)
> expect_equal(func(2, 2)$result, 5) # should give the required error...
Error: func(2, 2)$result not equal to 5.
1/1 mismatches
[1] 4 - 5 == -1

请注意这里更容易测试的优点。如果/当您决定更改打印功能内的格式时,您也不必更改结果计算正确的测试。

【讨论】:

    【解决方案2】:

    你可以使用这样的东西。

    func <- function(num1, num2){
        result = num1 + num2
        return(paste0(' ', as.character(Sys.time()), ':  with result: ', result))
    }
    
    library(testthat)
    

    使用字符串

    expect_equal(as.numeric(stringr::str_extract(func(2, 2), "[0-9]*\\.*[0-9]*$")), 4)
    expect_equal(as.numeric(stringr::str_extract(func(2, -2), "[0-9]*\\.*[0-9]+$")), 0)
    expect_equal(as.numeric(stringr::str_extract(func(15, 21.3), "[0-9]*\\.*[0-9]+$")), 36.3)
    

    使用基础 r

    expect_equal(as.numeric(regmatches(func(2, 2), regexpr("[0-9]*\\.*[0-9]*$", func(2, 2)))), 4)
    expect_equal(as.numeric(regmatches(func(2, -2), regexpr("[0-9]*\\.*[0-9]*$", func(2, -2)))), 0)
    expect_equal(as.numeric(regmatches(func(15, 21.3), regexpr("[0-9]*\\.*[0-9]*$",func(15, 21.3)))), 36.3)
    

    或者准确地测试函数的内部,但这取决于你的函数内部到底是什么。

    expect_equal(func(2, 2), paste0(' ', as.character(Sys.time()), ': with result: ', 4))
    expect_equal(func(2, -2), paste0(' ', as.character(Sys.time()), ': with result: ', 0))
    expect_equal(func(15, 21.3), paste0(' ', as.character(Sys.time()), ': with result: ', 36.3))
    

    【讨论】:

    • 我仍然说重写函数使其只返回结果和时间(比如在列表中)将是一种更简洁的方法。然后expect_equal(func(2,2)$result, 4) 可以为您提供您想要的,而不必从返回的字符串中解析所有结果。
    • "准确地测试函数内部,但这取决于你的函数内部到底是什么" 这很聪明。没想到这个……
    猜你喜欢
    • 1970-01-01
    • 2018-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 1970-01-01
    相关资源
    最近更新 更多