R6: 单元测试私有函数的推荐方法是什么?

创建于 2015-01-23  ·  11评论  ·  资料来源: r-lib/R6

相关: https :

最有用的评论

@zkurtz截至目前,您可以通过obj$.__enclos_env__$private访问私有环境。 例如:

A <- R6Class("A",
  private = list(x=1)
)
a <- A$new()

a$.__enclos_env__$private$x
#> [1] 1

最初提出这个问题时,我认为这是不可能的。

所有11条评论

这是我目前的做法:

MyClass <- R6Class(
  classname = "MyClass",
  public = list(
    initialize = function(
    ) {
      frames <- sys.frames()
      private$.private_env <- frames[[length(frames) - 1]]$private_bind_env
    },
    getPrivate = function() private$.private_env
  ),
  private = list(
    .private_env = "environment",
    foo = function() {
      "I'm a private function"
    }
  )
)

test_that("private/foo", {
  inst <- MyClass$new()
  expect_identical(inst$getPrivate()$foo(),
    "I'm a private function"
  )
})

想知道是否有更好/更“内置”的方式来对私有函数进行单元测试。

你可以像这样简化它:

MyClass <- R6Class(
  classname = "MyClass",
  public = list(
    getPrivate = function() private
  ),
  private = list(
    foo = function() "I'm a private function"
  )
)

library(testthat)
test_that("private/foo", {
  inst <- MyClass$new()
  expect_identical(inst$getPrivate()$foo(),
    "I'm a private function"
  )
})

但是当然这仍然涉及将getPrivate函数添加到您的类中,您通常可能不想这样做。

您实际上可以通过检查公共功能来访问私有环境。 在这个例子中,它使用environment(inst$pub)$private来获取私有环境:

MyClass <- R6Class(
  classname = "MyClass",
  public = list(
    pub = function() "I'm a public function"
  ),
  private = list(
    foo = function() "I'm a private function"
  )
)


test_that("private/foo", {
  inst <- MyClass$new()
  # Get the private environment
  priv <- environment(inst$pub)$private
  expect_identical(priv$foo(),
    "I'm a private function"
  )
})

这在https://github.com/wch/R6/blob/master/doc_extra/R6.pdf的第二页上有图解

啊,很不错! 谢谢指点!

相关: https :

对于我的 SO 问题,这次谈话似乎主要是“不”。 我想知道默认情况下分配“@”符号(或类似的东西)以提供对 R6 类中所有私有组件的内置访问会有多困难。 inst@foo会很方便。

@zkurtz截至目前,您可以通过obj$.__enclos_env__$private访问私有环境。 例如:

A <- R6Class("A",
  private = list(x=1)
)
a <- A$new()

a$.__enclos_env__$private$x
#> [1] 1

最初提出这个问题时,我认为这是不可能的。

我通常只是为此编写一个小的内部get_private()函数,并在测试中使用它。

这些都是不错的选择,但都有些笨拙。 我想我不清楚a$x (或a@x ,如果$必须为公众保留)是否有充分的理由不能或不应该被定义作为a$.__enclos_env__$private$x一个非常简短的版本,根据@wch的例子。

或者,回到我原来的 SO 帖子的意图,有一个“锁定”公共组件的选项可能很方便,这样如果b被锁定, x$b = 5将返回错误。

@zkurtz提出的快捷方式和锁定功能确实很好!

它以这种方式工作的原因是因为这就是$运算符在 R 中索引环境时的工作方式。 您可以定义自己的$ S3 方法来执行您想要的任何操作,但它会带来显着的性能损失,这对于您的用例来说可能是也可能是不可接受的。 R6 背后的主要动机之一是轻量级。

我不知道是否可以用@做同样的事情。

如果您想要一个“锁定”变量(只能从类内部更改),则必须使用活动绑定,如下所示:

A <- R6Class("A",
  active = list(
    x = function(value) {
      if (missing(value)) private$.x
      else stop("Can't assign value to x")
    }
  ),
  private = list(.x = 1)
)

a <- A$new()

a$x
#> [1] 1

a$x <- 2
#> Error in (function (value)  : Can't assign value to x

使用像这样的主动绑定会产生性能成本。

如果你想要一个真正锁定的值(不能从类内部或外部改变),你可以在初始化函数中使用lockBinding()

A <- R6Class("A",
  public = list(
    initialize = function() {
      lockBinding("x", self)
    },
    x = 1
  )
)

a <- A$new()

a$x
#> [1] 1

a$x <- 2
#> Error in a$x <- 2 : cannot change value of locked binding for 'x'

@wch 太棒了,我会试试的。 如果我理解正确, lockBinding正是我一直在寻找的(假设它没有大的性能问题)。

@zkurtz很棒。 我相信lockBinding没有任何性能成本。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

gaborcsardi picture gaborcsardi  ·  22评论

wch picture wch  ·  8评论

kenarab picture kenarab  ·  8评论

mb706 picture mb706  ·  4评论

paulstaab picture paulstaab  ·  3评论