Skip to content

Commit fe0a15b

Browse files
authored
Merge branch 'main' into subtests
2 parents e581491 + 596b3e4 commit fe0a15b

File tree

4 files changed

+82
-55
lines changed

4 files changed

+82
-55
lines changed

NEWS.md

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

33
* `test_that()`, `describe()`, and `it()` can now be arbitrarily nested. Each component will skip only if it and its subtests don't contain any expectations. The interactive stop reporter has been fixed so it doesn't duplicate failures. (#2063, #2188).
4+
* Test filtering now works with `it()`, and the `desc` argument can take a character vector in order to recursively filter subtests (i.e. `it()` nested inside of `describe()`) (#2118).
45
* New `snapshot_reject()` rejects all modified snapshots by deleting the `.new` variants (#1923).
56
* New `SlowReporter` makes it easier to find the slowest tests in your package. The easiest way to run it is with `devtools::test(reporter = "slow")` (#1466).
67
* Power `expect_mapequal()` with `waldo::compare(list_as_map = TRUE)` (#1521).

R/source.R

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
#' @param path Path to files.
66
#' @param pattern Regular expression used to filter files.
77
#' @param env Environment in which to evaluate code.
8-
#' @param desc If not-`NULL`, will run only test with this `desc`ription.
8+
#' @param desc A character vector used to filter tests. This is used to
9+
#' (recursively) filter the content of the file, so that only the non-test
10+
#' code up to and including the match test is run.
911
#' @param chdir Change working directory to `dirname(path)`?
1012
#' @param wrap Automatically wrap all code within [test_that()]? This ensures
1113
#' that all expectations are reported, even if outside a test block.
@@ -26,6 +28,7 @@ source_file <- function(
2628
if (!is.environment(env)) {
2729
stop_input_type(env, "an environment", call = error_call)
2830
}
31+
check_character(desc, allow_null = TRUE)
2932

3033
lines <- brio::read_lines(path)
3134
srcfile <- srcfilecopy(
@@ -73,50 +76,40 @@ source_file <- function(
7376
}
7477
}
7578

76-
filter_desc <- function(exprs, desc = NULL, error_call = caller_env()) {
77-
if (is.null(desc)) {
79+
filter_desc <- function(exprs, descs, error_call = caller_env()) {
80+
if (length(descs) == 0) {
7881
return(exprs)
7982
}
83+
desc <- descs[[1]]
8084

81-
found <- FALSE
82-
include <- rep(FALSE, length(exprs))
85+
subtest_idx <- which(unname(map_lgl(exprs, is_subtest)))
8386

84-
for (i in seq_along(exprs)) {
85-
expr <- exprs[[i]]
86-
87-
if (!is_call(expr, c("test_that", "describe"), n = 2)) {
88-
if (!found) {
89-
include[[i]] <- TRUE
90-
}
91-
} else {
92-
if (!is_string(expr[[2]])) {
93-
next
94-
}
95-
96-
test_desc <- as.character(expr[[2]])
97-
if (test_desc != desc) {
98-
next
99-
}
100-
101-
if (found) {
102-
cli::cli_abort(
103-
"Found multiple tests with specified description.",
104-
call = error_call
105-
)
106-
}
107-
include[[i]] <- TRUE
108-
found <- TRUE
109-
}
110-
}
111-
112-
if (!found) {
87+
matching_idx <- keep(subtest_idx, \(idx) exprs[[idx]][[2]] == desc)
88+
if (length(matching_idx) == 0) {
11389
cli::cli_abort(
114-
"Failed to find test with specified description.",
90+
"Failed to find test with description {.str {desc}}.",
91+
call = error_call
92+
)
93+
} else if (length(matching_idx) > 1) {
94+
cli::cli_abort(
95+
"Found multiple tests with description {.str {desc}}.",
11596
call = error_call
11697
)
11798
}
11899

119-
exprs[include]
100+
# Want all code up to and including the matching test, except for subtests
101+
keep_idx <- setdiff(seq2(1, matching_idx), setdiff(subtest_idx, matching_idx))
102+
# Recursively inspect the components of the subtest
103+
exprs[[matching_idx]][[3]] <- filter_desc(
104+
exprs[[matching_idx]][[3]],
105+
descs[-1],
106+
error_call = error_call
107+
)
108+
exprs[keep_idx]
109+
}
110+
111+
is_subtest <- function(expr) {
112+
is_call(expr, c("test_that", "describe", "it"), n = 2) && is_string(expr[[2]])
120113
}
121114

122115
#' @rdname source_file

tests/testthat/_snaps/source.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,6 @@
2626
Error:
2727
! `env` must be an environment, not the string "x".
2828

29-
# can find only matching test
30-
31-
Code
32-
filter_desc(code, "baz")
33-
Condition
34-
Error:
35-
! Failed to find test with specified description.
36-
3729
# preserve srcrefs
3830

3931
Code
@@ -43,11 +35,16 @@
4335
# this is a comment
4436
}))
4537

46-
# errors if duplicate labels
38+
# errors if zero or duplicate labels
4739

4840
Code
4941
filter_desc(code, "baz")
5042
Condition
5143
Error:
52-
! Found multiple tests with specified description.
44+
! Found multiple tests with description "baz".
45+
Code
46+
filter_desc(code, "missing")
47+
Condition
48+
Error:
49+
! Failed to find test with description "missing".
5350

tests/testthat/test-source.R

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,54 @@ test_that("checks its inputs", {
8282
})
8383
})
8484

85+
# filter_desc -------------------------------------------------------------
8586

86-
# filter_label -------------------------------------------------------------
87+
test_that("works with all tests types", {
88+
code <- exprs(
89+
test_that("foo", {}),
90+
describe("bar", {}),
91+
it("baz", {})
92+
)
93+
expect_equal(filter_desc(code, "foo"), code[1])
94+
expect_equal(filter_desc(code, "bar"), code[2])
95+
expect_equal(filter_desc(code, "baz"), code[3])
96+
})
8797

88-
test_that("can find only matching test", {
98+
test_that("only returns code before subtest", {
8999
code <- exprs(
90100
f(),
91-
test_that("foo", {}),
101+
describe("foo", {}),
92102
g(),
93-
describe("bar", {}),
94103
h()
95104
)
96105
expect_equal(filter_desc(code, "foo"), code[c(1, 2)])
97-
expect_equal(filter_desc(code, "bar"), code[c(1, 3, 4)])
98-
expect_snapshot(filter_desc(code, "baz"), error = TRUE)
106+
})
107+
108+
test_that("can select recursively", {
109+
code <- exprs(
110+
x <- 1,
111+
describe("a", {
112+
y <- 1
113+
describe("b", {
114+
z <- 1
115+
})
116+
y <- 2
117+
}),
118+
x <- 2
119+
)
120+
121+
expect_equal(
122+
filter_desc(code, c("a", "b")),
123+
exprs(
124+
x <- 1,
125+
describe("a", {
126+
y <- 1
127+
describe("b", {
128+
z <- 1
129+
})
130+
})
131+
)
132+
)
99133
})
100134

101135
test_that("preserve srcrefs", {
@@ -110,16 +144,18 @@ test_that("preserve srcrefs", {
110144
expect_snapshot(filter_desc(code, "foo"))
111145
})
112146

113-
114-
test_that("errors if duplicate labels", {
147+
test_that("errors if zero or duplicate labels", {
115148
code <- exprs(
116149
f(),
117150
test_that("baz", {}),
118151
test_that("baz", {}),
119152
g()
120153
)
121154

122-
expect_snapshot(filter_desc(code, "baz"), error = TRUE)
155+
expect_snapshot(error = TRUE, {
156+
filter_desc(code, "baz")
157+
filter_desc(code, "missing")
158+
})
123159
})
124160

125161
test_that("source_dir()", {

0 commit comments

Comments
 (0)