R6: Apa cara yang disarankan untuk menguji unit fungsi pribadi?

Dibuat pada 23 Jan 2015  ·  11Komentar  ·  Sumber: r-lib/R6

Komentar yang paling membantu

@zkurtz Sampai sekarang Anda dapat mengakses lingkungan pribadi melalui obj$.__enclos_env__$private . Sebagai contoh:

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

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

Ketika masalah ini awalnya diangkat, saya pikir itu tidak mungkin.

Semua 11 komentar

Itulah pendekatan saya saat ini:

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"
  )
})

Ingin tahu apakah ada cara "bawaan" yang lebih baik/lebih untuk menguji unit fungsi pribadi.

Anda dapat menyederhanakannya seperti ini:

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"
  )
})

Tapi tentu saja ini masih melibatkan penambahan fungsi getPrivate ke kelas Anda, yang mungkin tidak ingin Anda lakukan secara umum.

Anda sebenarnya dapat mengakses lingkungan pribadi dengan memeriksa fungsi publik. Dalam contoh ini, ia menggunakan environment(inst$pub)$private untuk mendapatkan lingkungan pribadi:

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"
  )
})

Ini digambarkan pada halaman kedua https://github.com/wch/R6/blob/master/doc_extra/R6.pdf. Diagram menggunakan gaya yang sama seperti yang ada di buku Advanced R Hadley.

Ah, sangat bagus! Terima kasih untuk penunjuknya!

Terkait: https://stackoverflow.com/questions/50222481/r-r6-class-as-a-rigid-read-only-data-structure

Percakapan ini tampaknya sebagian besar 'tidak' untuk pertanyaan SO saya. Saya bertanya-tanya betapa sulitnya menetapkan simbol '@' (atau sesuatu seperti itu) untuk menyediakan akses bawaan ke semua komponen pribadi di kelas R6 secara default. inst@foo akan cukup nyaman.

@zkurtz Sampai sekarang Anda dapat mengakses lingkungan pribadi melalui obj$.__enclos_env__$private . Sebagai contoh:

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

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

Ketika masalah ini awalnya diangkat, saya pikir itu tidak mungkin.

Saya biasanya hanya menulis fungsi get_private() internal kecil untuk ini, dan menggunakannya dalam pengujian.

Ini adalah opsi yang bagus, tetapi semuanya agak berat. Saya kira tidak jelas bagi saya apakah ada alasan bagus bahwa a$x (atau a@x , jika $ harus dicadangkan untuk publik) tidak dapat atau tidak boleh didefinisikan sebagai versi yang sangat singkat dari a$.__enclos_env__$private$x , sesuai dengan contoh @wch .

Atau, kembali ke maksud posting SO asli saya, mungkin berguna untuk memiliki opsi untuk 'mengunci' komponen publik, sehingga x$b = 5 akan mengembalikan kesalahan jika b terkunci.

Pintasan dan fitur penguncian seperti yang diusulkan oleh @zkurtz memang bagus!

Alasan cara kerjanya adalah karena begitulah cara operator $ bekerja di R saat mengindeks ke lingkungan. Anda dapat menentukan metode $ S3 Anda sendiri yang melakukan apa pun yang Anda inginkan, tetapi itu akan datang dengan penalti kinerja yang signifikan, yang mungkin atau mungkin tidak dapat diterima untuk kasus penggunaan Anda. Salah satu motivasi utama di balik R6 adalah menjadi ringan.

Saya tidak tahu apakah mungkin melakukan hal yang sama dengan @ .

Jika Anda ingin memiliki variabel "terkunci" (yang hanya dapat diubah dari dalam kelas), Anda harus menggunakan pengikatan aktif, seperti ini:

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

Menggunakan pengikatan aktif seperti ini akan memiliki biaya kinerja.

Jika Anda menginginkan nilai yang benar-benar terkunci (yang tidak dapat diubah dari dalam atau luar kelas), Anda dapat menggunakan lockBinding() dalam fungsi inisialisasi:

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 Luar biasa, saya akan mencobanya. Jika saya mengerti dengan benar, lockBinding persis seperti yang saya cari (dengan asumsi itu tidak memiliki masalah kinerja yang besar).

@zkurtz Hebat. Saya percaya bahwa lockBinding tidak memiliki biaya kinerja.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

mb706 picture mb706  ·  4Komentar

wch picture wch  ·  8Komentar

paulstaab picture paulstaab  ·  3Komentar

gaborcsardi picture gaborcsardi  ·  22Komentar

kenarab picture kenarab  ·  8Komentar