Skip to content

Commit 1750b4a

Browse files
committed
Deprecate with_mock() and local_mock()
Fixes #1999 Deprecate old mocking functions Fix news More news fixes Merge merge problem Check revdeps
1 parent 690fdb8 commit 1750b4a

File tree

11 files changed

+15814
-300
lines changed

11 files changed

+15814
-300
lines changed

NEWS.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
* Fixed an issue where calling `skip()` outside of an active test could
66
cause an unexpected error (@kevinushey, #2039).
7+
* `local_mock()` and `with_mock()` have been deprecated because they are no longer permitted in R 4.5.
78

89
# testthat 3.2.2
910

@@ -49,6 +50,62 @@
4950

5051
* `with_mock()` and `local_mock()` have been unconditionally deprecated as
5152
they will no longer work in future versions of R (#1999).
53+
* `with_mock()` and `local_mock()` have been unconditionally deprecated as they will no longer work in future versions of R (#1999).
54+
* `expect_condition()` and friends now include the `class` of the expected condition in the failure mesage, if used (#1987).
55+
* `LANGUAGE` is now set to `"C"` in reprocucible environments (i.e.
56+
`test_that()` blocks) to disable translations. This fixes warnings
57+
about being unable to set the language to `"en"` (#1925).
58+
* Fixed an issue where `expect_no_error(1)` was failing (#2037).
59+
60+
* Fixed an issue where calling `skip()` outside of an active test could
61+
cause an unexpected error (@kevinushey, #2039).
62+
63+
# testthat 3.2.2
64+
65+
## New expectations
66+
67+
* `expect_s7_class()` tests if an object is an S7 class (#1580).
68+
69+
* `expect_no_failure()`, `expect_no_success()` and `expect_snapshot_failure()`
70+
provide more options for testing expectations.
71+
72+
## Bug fixes and minor improvements
73+
74+
* testthat now requires waldo 0.6.0 or later to access the latest features
75+
(#1955).
76+
77+
* `expect_condition()` and related functions now include the `class` of the
78+
expected condition in the failure message, if provided (#1987).
79+
80+
* `expect_error()` and friends now error if you supply `...` but not `pattern`
81+
(#1932). They no longer give an uninformative error if they fail inside
82+
a magrittr pipe (#1994).
83+
84+
* `expect_no_*()` expectations no longer incorrectly emit a passing test result
85+
if they in fact fail (#1997).
86+
87+
* `expect_setequal()` correctly identifies what is missing where (#1962).
88+
89+
* `expect_snapshot()` now strips line breaks in test descriptions
90+
(@LDSamson, #1900), and errors when called from a `test_that()` that has an
91+
empty description (@kevinushey, #1980).
92+
93+
* `expect_true()` and `expect_false()` give better errors if `actual` isn't a
94+
vector (#1996).
95+
96+
* `expect_visible()` and `expect_invisible()` have clearer failure messages
97+
(#1966).
98+
* `local_reproducible_output()` (used in `test_that()` blocks) now sets
99+
`LANGUAGE` to `"C"` instead of `"en"` to disable translations,
100+
avoiding warnings on some platforms (#1925).
101+
102+
* `skip_if_not_installed()` generates a clearer message that sorts better
103+
(@MichaelChirico, #1959).
104+
105+
* `with_mock()` and `local_mock()` have been unconditionally deprecated as
106+
they will no longer work in future versions of R (#1999).
107+
108+
* `local_mock()` and `with_mock()` have been deprecated because they are no longer permitted in R 4.5.
52109

53110
# testthat 3.2.1
54111

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)