Skip to content

Commit 6ba4397

Browse files
hadleyDavisVaughan
andauthored
Collect more reliable trace_env (#2012)
And create more helpers for testing various more complicated success and failure modes. Fixes #1994 --------- Co-authored-by: Davis Vaughan <[email protected]>
1 parent edee55a commit 6ba4397

File tree

9 files changed

+124
-48
lines changed

9 files changed

+124
-48
lines changed

NAMESPACE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,10 @@ export(expect_more_than)
106106
export(expect_named)
107107
export(expect_no_condition)
108108
export(expect_no_error)
109+
export(expect_no_failure)
109110
export(expect_no_match)
110111
export(expect_no_message)
112+
export(expect_no_success)
111113
export(expect_no_warning)
112114
export(expect_null)
113115
export(expect_output)
@@ -119,6 +121,7 @@ export(expect_setequal)
119121
export(expect_silent)
120122
export(expect_snapshot)
121123
export(expect_snapshot_error)
124+
export(expect_snapshot_failure)
122125
export(expect_snapshot_file)
123126
export(expect_snapshot_output)
124127
export(expect_snapshot_value)

NEWS.md

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

3+
* New `expect_no_failure()`, `expect_no_success()` and `expect_snapshot_failure()` provide more options for testing expectations.
4+
* `expect_error()` and friends no longer give an uninformative error if they fail inside a magrittr pipe (#1994).
35
* `expect_setequal()` correctly identifies what is missing where (#1962).
46
* `expect_true()` and `expect_false()` give better errors if `actual` isn't a vector (#1996).
57
* `expect_no_*()` expectations no longer incorrectly emit a passing test result if they in fact fail (#1997).

R/expect-condition.R

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,7 @@ expect_warning <- function(object,
163163
...,
164164
inherit = inherit,
165165
info = info,
166-
label = label,
167-
trace_env = caller_env()
166+
label = label
168167
)
169168
} else {
170169
act <- quasi_capture(enquo(object), label, capture_warnings, ignore_deprecation = identical(regexp, NA))
@@ -196,8 +195,7 @@ expect_message <- function(object,
196195
...,
197196
inherit = inherit,
198197
info = info,
199-
label = label,
200-
trace_env = caller_env()
198+
label = label
201199
)
202200
} else {
203201
act <- quasi_capture(enquo(object), label, capture_messages)
@@ -225,8 +223,7 @@ expect_condition <- function(object,
225223
...,
226224
inherit = inherit,
227225
info = info,
228-
label = label,
229-
trace_env = caller_env()
226+
label = label
230227
)
231228
} else {
232229

@@ -256,6 +253,7 @@ expect_condition_matching <- function(base_class,
256253
label = NULL,
257254
trace_env = caller_env(),
258255
error_call = caller_env()) {
256+
259257
check_dots_used(error = function(cnd) {
260258
warn(conditionMessage(cnd), call = error_call)
261259
})
@@ -267,7 +265,7 @@ expect_condition_matching <- function(base_class,
267265
...,
268266
inherit = inherit,
269267
ignore_deprecation = base_class == "warning" && identical(regexp, NA),
270-
error_call = trace_env
268+
error_call = error_call
271269
)
272270

273271
act <- quasi_capture(

R/expect-no-condition.R

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,10 @@ expect_no_condition <- function(object,
8282

8383

8484
expect_no_ <- function(base_class,
85-
object,
86-
regexp = NULL,
87-
class = NULL,
88-
error_call = caller_env()) {
85+
object,
86+
regexp = NULL,
87+
class = NULL,
88+
trace_env = caller_env()) {
8989

9090
matcher <- cnd_matcher(
9191
base_class,
@@ -116,7 +116,7 @@ expect_no_ <- function(base_class,
116116
indent_lines(rlang::cnd_message(cnd))
117117
)
118118
message <- format_error_bullets(c(expected, i = actual))
119-
fail(message, trace_env = error_call)
119+
fail(message, trace_env = trace_env)
120120
}
121121
)
122122
}

R/expect-self-test.R

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,56 @@
1+
capture_failure <- new_capture("expectation_failure")
2+
capture_success <- function(expr) {
3+
cnd <- NULL
4+
5+
withCallingHandlers(
6+
expr,
7+
expectation_failure = function(cnd) {
8+
invokeRestart("continue_test")
9+
},
10+
expectation_success = function(cnd) {
11+
cnd <<- cnd
12+
}
13+
)
14+
cnd
15+
}
16+
17+
new_capture("expectation_success")
18+
119
#' Tools for testing expectations
220
#'
3-
#' Use these expectations to test other expectations.
21+
#' @description
22+
#' * `expect_sucess()` 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.
28+
#'
429
#' Use `show_failure()` in examples to print the failure message without
530
#' throwing an error.
631
#'
7-
#' @param expr Expression that evaluates a single expectation.
32+
#' @param expr Code to evalute
833
#' @param message Check that the failure message matches this regexp.
934
#' @param ... Other arguments passed on to [expect_match()].
1035
#' @export
1136
expect_success <- function(expr) {
12-
exp <- capture_expectation(expr)
37+
exp <- capture_success(expr)
1338

1439
if (is.null(exp)) {
15-
fail("no expectation used.")
16-
} else if (!expectation_success(exp)) {
17-
fail(paste0(
18-
"Expectation did not succeed:\n",
19-
exp$message
20-
))
40+
fail("Expectation did not succeed")
41+
} else {
42+
succeed()
43+
}
44+
invisible(NULL)
45+
}
46+
47+
#' @export
48+
#' @rdname expect_success
49+
expect_no_success <- function(expr) {
50+
exp <- capture_success(expr)
51+
52+
if (!is.null(exp)) {
53+
fail("Expectation succeeded")
2154
} else {
2255
succeed()
2356
}
@@ -27,19 +60,31 @@ expect_success <- function(expr) {
2760
#' @export
2861
#' @rdname expect_success
2962
expect_failure <- function(expr, message = NULL, ...) {
30-
exp <- capture_expectation(expr)
63+
exp <- capture_failure(expr)
3164

3265
if (is.null(exp)) {
33-
fail("No expectation used")
34-
return()
35-
}
36-
if (!expectation_failure(exp)) {
3766
fail("Expectation did not fail")
38-
return()
67+
} else if (!is.null(message)) {
68+
expect_match(exp$message, message, ...)
69+
} else {
70+
succeed()
3971
}
72+
invisible(NULL)
73+
}
4074

41-
if (!is.null(message)) {
42-
expect_match(exp$message, message, ...)
75+
#' @export
76+
#' @rdname expect_success
77+
expect_snapshot_failure <- function(expr) {
78+
expect_snapshot_error(expr, "expectation_failure")
79+
}
80+
81+
#' @export
82+
#' @rdname expect_success
83+
expect_no_failure <- function(expr) {
84+
exp <- capture_failure(expr)
85+
86+
if (!is.null(exp)) {
87+
fail("Expectation failed")
4388
} else {
4489
succeed()
4590
}
@@ -67,10 +112,6 @@ show_failure <- function(expr) {
67112
invisible()
68113
}
69114

70-
expect_snapshot_failure <- function(x) {
71-
expect_snapshot_error(x, "expectation_failure")
72-
}
73-
74115
expect_snapshot_reporter <- function(reporter, paths = test_path("reporters/tests.R")) {
75116
local_options(rlang_trace_format_srcrefs = FALSE)
76117
local_rng_version("3.3")

man/expect_success.Rd

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

tests/testthat/test-expect-condition.R

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ test_that("can capture Throwable conditions from rJava", {
7676
expect_error(throw("foo"), "foo", class = "Throwable")
7777
})
7878

79+
test_that("capture correct trace_env (#1994)", {
80+
# This should fail, not error
81+
expect_failure(expect_error(stop("oops")) %>% expect_warning())
82+
expect_failure(expect_warning(expect_error(stop("oops"))))
83+
})
84+
7985
# expect_warning() ----------------------------------------------------------
8086

8187
test_that("warnings are converted to errors when options('warn') >= 2", {

tests/testthat/test-expect-no-condition.R

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,13 @@ test_that("expect_no_* conditions behave as expected", {
1414
})
1515

1616
test_that("expect_no_* don't emit success when they fail", {
17+
expect_no_success(expect_no_error(stop("!")))
18+
})
1719

18-
catch_cnds <- function(code) {
19-
cnds <- list()
20-
21-
withCallingHandlers(code, condition = function(cnd) {
22-
cnds[[length(cnds) + 1]] <<- cnd
23-
invokeRestart("continue_test")
24-
})
25-
cnds
26-
}
27-
28-
cnds <- catch_cnds(expect_no_error(stop("!")))
29-
expect_length(cnds, 1)
30-
expect_s3_class(cnds[[1]], "expectation_failure")
20+
test_that("capture correct trace_env (#1994)", {
21+
# This should fail, not error
22+
expect_failure(expect_message({message("a"); warn("b")}) %>% expect_no_warning())
23+
expect_failure(expect_no_message({message("a"); warn("b")}) %>% expect_warning())
3124
})
3225

3326
test_that("unmatched conditions bubble up", {

tests/testthat/test-expect-self-test.R

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,19 @@ test_that("show_failure", {
2727
expect_null(show_failure(NULL))
2828
expect_output(show_failure(expect_true(FALSE)), "FALSE is not TRUE")
2929
})
30+
31+
test_that("can test for presence and absense of failure", {
32+
expect_success(expect_failure(fail()))
33+
expect_success(expect_no_failure(succeed()))
34+
35+
expect_failure(expect_failure(succeed()))
36+
expect_failure(expect_no_failure(fail()))
37+
})
38+
39+
test_that("can test for presence and absense of success", {
40+
expect_success(expect_success(succeed()))
41+
expect_success(expect_no_success(fail()))
42+
43+
expect_failure(expect_success(fail()))
44+
expect_failure(expect_no_success(succeed()))
45+
})

0 commit comments

Comments
 (0)