Skip to content

Commit c967250

Browse files
committed
Deprecate with_mock() and local_mock()
Fixes #1999
1 parent 690fdb8 commit c967250

File tree

11 files changed

+15759
-300
lines changed

11 files changed

+15759
-300
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
* `local_mock()` and `with_mock()` have been deprecated because they are no longer permitted in R 4.5.
2+
13
# testthat (development version)
24

35
* Fixed an issue where `expect_no_error(1)` was failing (#2037).

R/mock.R

Lines changed: 8 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
#' Mock functions in a package.
22
#'
33
#' @description
4-
#' `r lifecycle::badge("deprecated")`
5-
#'
64
#' `with_mock()` and `local_mock()` are deprecated in favour of
75
#' [with_mocked_bindings()] and [local_mocked_bindings()].
86
#'
97
#' These functions worked by using some C code to temporarily modify the mocked
10-
#' function _in place_. This was an abuse of R's internals and it is no longer
8+
#' function _in place_. This was an abuse of R's internals and it is no longer
119
#' permitted.
1210
#'
11+
#' @section 3rd edition:
12+
#' `r lifecycle::badge("deprecated")`
13+
#'
14+
#' `with_mock()` and `local_mock()` are deprecated in the third edition.
15+
#'
1316
#' @param ... named parameters redefine mocked functions, unnamed parameters
1417
#' will be evaluated after mocking the functions
1518
#' @param .env the environment in which to patch the functions,
@@ -21,116 +24,11 @@
2124
#' @return The result of the last unnamed parameter
2225
#' @export
2326
with_mock <- function(..., .env = topenv()) {
24-
lifecycle::deprecate_warn("3.3.0", "with_mock()", "with_mocked_bindings()")
25-
26-
dots <- eval(substitute(alist(...)))
27-
mock_qual_names <- names(dots)
28-
29-
if (all(mock_qual_names == "")) {
30-
warning(
31-
"Not mocking anything. Please use named parameters to specify the functions you want to mock.",
32-
call. = FALSE
33-
)
34-
code_pos <- rep(TRUE, length(dots))
35-
} else {
36-
code_pos <- (mock_qual_names == "")
37-
}
38-
code <- dots[code_pos]
39-
40-
mock_funs <- lapply(dots[!code_pos], eval, parent.frame())
41-
mocks <- extract_mocks(mock_funs, .env = .env)
42-
43-
on.exit(lapply(mocks, reset_mock), add = TRUE)
44-
lapply(mocks, set_mock)
45-
46-
# Evaluate the code
47-
if (length(code) > 0) {
48-
for (expression in code[-length(code)]) {
49-
eval(expression, parent.frame())
50-
}
51-
# Isolate last item for visibility
52-
eval(code[[length(code)]], parent.frame())
53-
}
27+
lifecycle::deprecate_stop("3.3.0", "with_mock()", "with_mocked_bindings()")
5428
}
5529

5630
#' @export
5731
#' @rdname with_mock
5832
local_mock <- function(..., .env = topenv(), .local_envir = parent.frame()) {
59-
lifecycle::deprecate_warn("3.3.0", "local_mock()", "local_mocked_bindings()")
60-
61-
mocks <- extract_mocks(list(...), .env = .env)
62-
on_exit <- bquote(
63-
on.exit(lapply(.(mocks), .(reset_mock)), add = TRUE),
64-
)
65-
66-
lapply(mocks, set_mock)
67-
eval_bare(on_exit, .local_envir)
68-
invisible()
69-
}
70-
71-
pkg_rx <- ".*[^:]"
72-
colons_rx <- "::(?:[:]?)"
73-
name_rx <- ".*"
74-
pkg_and_name_rx <- sprintf("^(?:(%s)%s)?(%s)$", pkg_rx, colons_rx, name_rx)
75-
76-
extract_mocks <- function(funs, .env) {
77-
if (is.environment(.env)) {
78-
.env <- environmentName(.env)
79-
}
80-
mock_qual_names <- names(funs)
81-
82-
lapply(
83-
stats::setNames(nm = mock_qual_names),
84-
function(qual_name) {
85-
pkg_name <- gsub(pkg_and_name_rx, "\\1", qual_name)
86-
87-
if (is_base_pkg(pkg_name)) {
88-
stop(
89-
"Can't mock functions in base packages (", pkg_name, ")",
90-
call. = FALSE
91-
)
92-
}
93-
94-
name <- gsub(pkg_and_name_rx, "\\2", qual_name)
95-
96-
if (pkg_name == "") {
97-
pkg_name <- .env
98-
}
99-
100-
env <- asNamespace(pkg_name)
101-
102-
if (!exists(name, envir = env, mode = "function")) {
103-
stop("Function ", name, " not found in environment ",
104-
environmentName(env), ".",
105-
call. = FALSE
106-
)
107-
}
108-
mock(name = name, env = env, new = funs[[qual_name]])
109-
}
110-
)
111-
}
112-
113-
mock <- function(name, env, new) {
114-
target_value <- get(name, envir = env, mode = "function")
115-
structure(
116-
list(
117-
env = env,
118-
name = as.name(name),
119-
orig_value = .Call(duplicate_, target_value), target_value = target_value,
120-
new_value = new
121-
),
122-
class = "mock"
123-
)
124-
}
125-
126-
set_mock <- function(mock) {
127-
.Call(reassign_function, mock$name, mock$env, mock$target_value, mock$new_value)
128-
}
129-
130-
reset_mock <- function(mock) {
131-
.Call(reassign_function, mock$name, mock$env, mock$target_value, mock$orig_value)
132-
}
133-
134-
is_base_pkg <- function(x) {
135-
x %in% rownames(utils::installed.packages(priority = "base"))
33+
lifecycle::deprecate_stop("3.3.0", "local_mock()", "local_mocked_bindings()")
13634
}

0 commit comments

Comments
 (0)