Skip to content

Commit 0a4e2b9

Browse files
committed
Merged origin/main into use-fail
2 parents b44f6c1 + cc79388 commit 0a4e2b9

15 files changed

+288
-144
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# testthat (development version)
22

3+
* `expect_no_failures()` and `expect_no_successes()` are now deprecated as `expect_success()` now test for no failures and `expect_failure()` tests for no successes (#)
34
* New `pass()` function to use in place of `succeed()` (#2113).
45
* `expectation()` is now a combination of `new_expectation()` and `exp_signal()` (#2125).
56
* `is_null()`/`matches()` deprecated in 2.0.0 (2017-12-19) and `is_true()`/`is_false()` deprecated in 2.1.0 (2019-04-23) have been removed (#2109).

R/expect-self-test.R

Lines changed: 90 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,43 @@
1-
capture_failure <- new_capture("expectation_failure")
2-
capture_success <- function(expr) {
1+
capture_success_failure <- function(expr) {
32
cnd <- NULL
43

5-
withCallingHandlers(
6-
expr,
7-
expectation_failure = function(cnd) {
8-
invokeRestart("continue_test")
9-
},
10-
expectation_success = function(cnd) {
11-
cnd <<- cnd
12-
}
4+
n_success <- 0
5+
n_failure <- 0
6+
7+
last_failure <- NULL
8+
9+
withRestarts(
10+
withCallingHandlers(
11+
expr,
12+
expectation_failure = function(cnd) {
13+
last_failure <<- cnd
14+
n_failure <<- n_failure + 1
15+
# Finish the test without bubbling up
16+
invokeRestart("failed")
17+
},
18+
expectation_success = function(cnd) {
19+
n_success <<- n_success + 1
20+
# Don't bubble up to any other handlers
21+
invokeRestart("continue_test")
22+
}
23+
),
24+
failed = function() {}
1325
)
14-
cnd
15-
}
1626

17-
new_capture("expectation_success")
27+
list(
28+
n_success = n_success,
29+
n_failure = n_failure,
30+
last_failure = last_failure
31+
)
32+
}
1833

1934
#' Tools for testing expectations
2035
#'
2136
#' @description
22-
#' * `expect_success()` and `expect_failure()` check that there's at least
23-
#' one success or failure respectively.
24-
#' * `expect_snapshot_failure()` records the failure message so that you can
25-
#' manually check that it is informative.
26-
#' * `expect_no_success()` and `expect_no_failure()` check that are no
27-
#' successes or failures.
37+
#' `expect_success()` checks that there's exactly one success and no failures;
38+
#' `expect_failure()` checks that there's exactly one failure and no successes.
39+
#' `expect_snapshot_failure()` records the failure message so that you can
40+
#' manually check that it is informative.
2841
#'
2942
#' Use `show_failure()` in examples to print the failure message without
3043
#' throwing an error.
@@ -34,42 +47,50 @@ new_capture("expectation_success")
3447
#' @param ... Other arguments passed on to [expect_match()].
3548
#' @export
3649
expect_success <- function(expr) {
37-
exp <- capture_success(expr)
50+
status <- capture_success_failure(expr)
3851

39-
if (is.null(exp)) {
52+
if (status$n_success == 0) {
4053
fail("Expectation did not succeed")
41-
} else {
42-
succeed()
54+
} else if (status$n_success > 1) {
55+
fail(sprintf(
56+
"Expectation succeeded %i times, instead of once",
57+
status$n_success
58+
))
4359
}
44-
invisible(NULL)
45-
}
46-
47-
#' @export
48-
#' @rdname expect_success
49-
expect_no_success <- function(expr) {
50-
exp <- capture_success(expr)
5160

52-
if (!is.null(exp)) {
53-
fail("Expectation succeeded")
54-
} else {
55-
succeed()
61+
if (status$n_failure > 0) {
62+
fail(sprintf(
63+
"Expectation failed %i times, instead of zero",
64+
status$n_failure
65+
))
5666
}
57-
invisible(NULL)
67+
68+
pass(NULL)
5869
}
5970

6071
#' @export
6172
#' @rdname expect_success
6273
expect_failure <- function(expr, message = NULL, ...) {
63-
exp <- capture_failure(expr)
74+
status <- capture_success_failure(expr)
6475

65-
if (is.null(exp)) {
76+
if (status$n_failure == 0) {
6677
fail("Expectation did not fail")
67-
} else if (!is.null(message)) {
68-
expect_match(exp$message, message, ...)
69-
} else {
70-
succeed()
78+
} else if (status$n_failure > 1) {
79+
# This should be impossible, but including for completeness
80+
fail("Expectation failed more than once")
7181
}
72-
invisible(NULL)
82+
83+
if (status$n_success != 0) {
84+
fail(sprintf(
85+
"Expectation succeeded %i times, instead of never",
86+
status$n_success
87+
))
88+
}
89+
90+
if (!is.null(message)) {
91+
return(expect_match(status$last_failure$message, message, ...))
92+
}
93+
pass(NULL)
7394
}
7495

7596
#' @export
@@ -78,12 +99,36 @@ expect_snapshot_failure <- function(expr) {
7899
expect_snapshot_error(expr, "expectation_failure")
79100
}
80101

102+
#' Test for absence of success or failure
103+
#'
104+
#' @description
105+
#' `r lifecycle::badge("deprecated")`
106+
#'
107+
#' These functions are deprecated because [expect_success()] and
108+
#' [expect_failure()] now test for exactly one success or no failures, and
109+
#' exactly one failure and no successes.
110+
#'
111+
#' @keywords internal
81112
#' @export
82-
#' @rdname expect_success
113+
expect_no_success <- function(expr) {
114+
lifecycle::deprecate_warn("3.3.0", "expect_no_success()", "expect_failure()")
115+
status <- capture_success_failure(expr)
116+
117+
if (status$n_success > 0) {
118+
fail("Expectation succeeded")
119+
} else {
120+
succeed()
121+
}
122+
invisible(NULL)
123+
}
124+
125+
#' @export
126+
#' @rdname expect_no_success
83127
expect_no_failure <- function(expr) {
84-
exp <- capture_failure(expr)
128+
lifecycle::deprecate_warn("3.3.0", "expect_no_failure()", "expect_success()")
129+
status <- capture_success_failure(expr)
85130

86-
if (!is.null(exp)) {
131+
if (status$n_failure > 0) {
87132
fail("Expectation failed")
88133
} else {
89134
succeed()

R/expect-that.R

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,3 @@
1-
#' Expect that a condition holds.
2-
#'
3-
#' @description
4-
#' `r lifecycle::badge("superseded")`
5-
#'
6-
#' An old style of testing that's no longer encouraged.
7-
#'
8-
#' @section 3rd edition:
9-
#' `r lifecycle::badge("deprecated")`
10-
#'
11-
#' This style of testing is formally deprecated as of the 3rd edition.
12-
#' Use a more specific `expect_` function instead.
13-
#'
14-
#' @param object Object to test.
15-
#'
16-
#' Supports limited unquoting to make it easier to generate readable failures
17-
#' within a function or for loop. See [quasi_label] for more details.
18-
#' @param condition, a function that returns whether or not the condition
19-
#' is met, and if not, an error message to display.
20-
#' @param label Used to customise failure messages. For expert use only.
21-
#' @param info Extra information to be included in the message. This argument
22-
#' is soft-deprecated and should not be used in new code. Instead see
23-
#' alternatives in [quasi_label].
24-
#' @return the (internal) expectation result as an invisible list
25-
#' @keywords internal
26-
#' @export
27-
#' @seealso [fail()] for an expectation that always fails.
28-
#' @examples
29-
#' expect_that(5 * 2, equals(10))
30-
#' expect_that(sqrt(2) ^ 2, equals(2))
31-
#' \dontrun{
32-
#' expect_that(sqrt(2) ^ 2, is_identical_to(2))
33-
#' }
34-
expect_that <- function(object, condition, info = NULL, label = NULL) {
35-
edition_deprecate(3, "expect_that()")
36-
condition(object)
37-
}
38-
391
#' `pass()` or `fail()` a test
402
#'
413
#' @description

R/old-school.R

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,41 @@
1+
#' Expect that a condition holds.
2+
#'
3+
#' @description
4+
#' `r lifecycle::badge("superseded")`
5+
#'
6+
#' An old style of testing that's no longer encouraged.
7+
#'
8+
#' @section 3rd edition:
9+
#' `r lifecycle::badge("deprecated")`
10+
#'
11+
#' This style of testing is formally deprecated as of the 3rd edition.
12+
#' Use a more specific `expect_` function instead.
13+
#'
14+
#' @param object Object to test.
15+
#'
16+
#' Supports limited unquoting to make it easier to generate readable failures
17+
#' within a function or for loop. See [quasi_label] for more details.
18+
#' @param condition, a function that returns whether or not the condition
19+
#' is met, and if not, an error message to display.
20+
#' @param label Used to customise failure messages. For expert use only.
21+
#' @param info Extra information to be included in the message. This argument
22+
#' is soft-deprecated and should not be used in new code. Instead see
23+
#' alternatives in [quasi_label].
24+
#' @return the (internal) expectation result as an invisible list
25+
#' @keywords internal
26+
#' @export
27+
#' @seealso [fail()] for an expectation that always fails.
28+
#' @examples
29+
#' expect_that(5 * 2, equals(10))
30+
#' expect_that(sqrt(2) ^ 2, equals(2))
31+
#' \dontrun{
32+
#' expect_that(sqrt(2) ^ 2, is_identical_to(2))
33+
#' }
34+
expect_that <- function(object, condition, info = NULL, label = NULL) {
35+
edition_deprecate(3, "expect_that()")
36+
condition(object)
37+
}
38+
139
#' Old-style expectations.
240
#'
341
#' @description

R/try-again.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,5 @@ try_again <- function(times, code) {
4444
times <- times - 1L
4545
}
4646

47-
stop(e)
47+
exp_signal(e)
4848
}

man/expect_no_success.Rd

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/expect_success.Rd

Lines changed: 3 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/expect_that.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# errors in expect_success bubble up
2+
3+
Code
4+
expect_success(abort("error"))
5+
Condition
6+
Error:
7+
! error
8+
9+
# expect_no are deprecated
10+
11+
Code
12+
expect_no_failure(succeed())
13+
Condition
14+
Warning:
15+
`expect_no_failure()` was deprecated in testthat 3.3.0.
16+
i Please use `expect_success()` instead.
17+
Code
18+
expect_no_success(fail())
19+
Condition
20+
Warning:
21+
`expect_no_success()` was deprecated in testthat 3.3.0.
22+
i Please use `expect_failure()` instead.
23+

tests/testthat/test-expect-condition.R

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,17 @@ test_that("can capture Throwable conditions from rJava", {
7878

7979
test_that("capture correct trace_env (#1994)", {
8080
# This should fail, not error
81-
expect_failure(expect_error(stop("oops")) |> expect_warning())
82-
expect_failure(expect_warning(expect_error(stop("oops"))))
81+
status <- capture_success_failure({
82+
stop("oops") |> expect_error() |> expect_warning()
83+
})
84+
expect_equal(status$n_success, 1) # from expect_error()
85+
expect_equal(status$n_failure, 1) # from expect_warning()
86+
87+
status <- capture_success_failure({
88+
stop("oops") %>% expect_error() %>% expect_warning()
89+
})
90+
expect_equal(status$n_success, 1) # from expect_error()
91+
expect_equal(status$n_failure, 1) # from expect_warning()
8392
})
8493

8594
# expect_warning() ----------------------------------------------------------

0 commit comments

Comments
 (0)