Как имитировать функции из базы?
Я testthat
функцию из базы в своем коде, и я хочу издеваться над этой функцией в своем testthat
модуле testthat
.
Как я могу это сделать?
library(testthat)
my.func <- function() {
return(Sys.info()["sysname"]) # e. g. "Linux"
}
my.func()
# sysname
# "Linux"
test_that("base function can be mocked",
with_mock(
Sys.info = function() return(list(sysname = "Clever OS")), # see edit 2 !!!
expect_equal(my.func(), "Clever OS", fixed = TRUE)
)
)
# Error: Test failed: 'base function can be mocked'
# * my.func() not equal to "Clever OS".
?with_mock
говорит:
Функции в базовых пакетах нельзя издеваться, но это можно легко обойти, указав функцию обертки.
Я мог бы инкапсулировать вызов базовой функции Sys.info()
с помощью функции-оболочки, которую я вызываю из my.func
но пусть предполагается, что я не могу этого сделать, потому что я тестирую функцию из пакета, который я не могу изменить...
Любое решение для этого?
Я использую R3.4.4 64 бит на Ubuntu 14.04 с testthat 2.0.0.9000.
Изменить 1:
С помощью
'base::Sys.info' = function() return(list(sysname = "Clever OS"))
приводит к ошибке testthat
msg:
Невозможно высмеять функции в базовых пакетах (базовых)
Редактировать 2: Поскольку @suren показывает в своем ответе, мой пример кода здесь неверен (функция издевательства возвращает другой класс, а затем оригинал :-(
Правильная функция mock должна быть: Sys.info = function() return(c(sysname = "Clever OS"))
Ответы
Ответ 1
Сообщение об ошибке - my.func(), не равное "Умной ОС".. Причина в том, что Sys.info
возвращает именованный вектор символов, а ваша издевательская функция - list
.
Просто измените насмешливую функцию и ожидаемое значение, и оно работает:
test_that("base function can be mocked",
with_mock(
Sys.info = function() return(c(sysname = "Clever OS")),
expect_equal(my.func(), c(sysname = "Clever OS"), fixed = TRUE)
)
)
Это работает даже внутри пакета.
Примечание. Отказывание базовых функций не должно работать в соответствии с помощью with_mock
но оно выполняется (по крайней мере, на данный момент).
Следующий (my.func() $ 'sysname'), похоже, передает тест с исходным кодом из вопроса.
test_that("base function can be mocked",
with_mock(
Sys.info = function() return(list(sysname = "Clever OS")),
expect_equal(my.func()$'sysname', "Clever OS", fixed = TRUE)
)
)
Кроме того, у вас есть список, в котором строка в expect_equal
test_that("base function can be mocked",
with_mock(
Sys.info = function() return(list(sysname = "Clever OS")),
expect_equal(my.func(), list('sysname' = "Clever OS"), fixed = TRUE)
)
)
Ответ 2
Предупреждение об издевательских базовых функциях с помощью with_mock
:
Несмотря на то, что издевательство над базовыми функциями может работать в случае моего вопроса, а принятый ответ @Suren вызывает много проблем вокруг with_mock
в testthat
пакете, чтобы отказаться от фальсификации функций базового пакета (или даже функций вне тестируемого пакета), например
testthat 2.0.0 - Нарушение API-интерфейса
- "Невозможно высмеять функции в базовых пакетах": вы больше не можете использовать with_mock() для издевательства функций в базовых пакетах, потому что это больше не работает в R-девеле из-за изменений с помощью компилятора байтового кода.
mockr
этого я рекомендую использовать mockery
или mockr
.
Не допускайте издевательства над базовыми пакетами
Функция with_mock(), похоже, плохо взаимодействует с JIT-компилятором
- Как я уже упоминал в hadley/testthat # 546 (комментарий), в издевательстве уже есть функция замены для to_mock, называемая stub, которая использует другой подход к издевательствам, который не ограничен проблемами, возникающими в других потоках. Если с_моклом ломается, я думаю, что этот пакет будет работать так же с насмешкой. Я также думаю, что издевательство было бы разумным местом для размещения with_mock, где он мог бы жить вместе с заглушкой по наследственным причинам.
Предотвращать с помощью__mock от касания базовых пакетов R