Skip to content

Commit 2b1a8ca

Browse files
committed
Re-implement and polish
1 parent 6d165ab commit 2b1a8ca

File tree

5 files changed

+69
-127
lines changed

5 files changed

+69
-127
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+
* 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).
34
* 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).
45
* Power `expect_mapequal()` with `waldo::compare(list_as_map = TRUE)` (#1521).
56
* On CRAN, `test_that()` now automatically skips if a package is not installed (#1585). Practically, this means that you no longer need to check that suggested packages are installed. (We don't do this in the tidyverse because we think it has limited payoff, but other styles advise differently.)

R/source.R

Lines changed: 15 additions & 16 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.
@@ -41,7 +43,7 @@ source_file <- function(
4143
con <- textConnection(lines, encoding = "UTF-8")
4244
withr::defer(try(close(con), silent = TRUE))
4345
exprs <- parse(con, n = -1, srcfile = srcfile, encoding = "UTF-8")
44-
exprs <- filter_subtests(exprs, desc, error_call = error_call)
46+
exprs <- filter_desc(exprs, desc, error_call = error_call)
4547

4648
n <- length(exprs)
4749
if (n == 0L) {
@@ -74,34 +76,32 @@ source_file <- function(
7476
}
7577
}
7678

77-
filter_subtests <- function(exprs, descs, error_call = caller_env()) {
79+
filter_desc <- function(exprs, descs, error_call = caller_env()) {
7880
if (length(descs) == 0) {
7981
return(exprs)
8082
}
83+
desc <- descs[[1]]
8184

82-
is_subtest <- unname(map_lgl(exprs, is_subtest))
83-
84-
subtest_idx <- which(is_subtest)
85-
code_idx <- which(!is_subtest)
86-
matching_idx <- keep(subtest_idx, \(idx) {
87-
exprs[[idx]][[2]] == descs[[1]]
88-
})
85+
subtest_idx <- which(unname(map_lgl(exprs, is_subtest)))
8986

87+
matching_idx <- keep(subtest_idx, \(idx) exprs[[idx]][[2]] == desc)
9088
if (length(matching_idx) == 0) {
9189
cli::cli_abort(
92-
"Failed to find test with specified description",
90+
"Failed to find test with description {.str {desc}}.",
9391
call = error_call
9492
)
9593
} else if (length(matching_idx) > 1) {
9694
cli::cli_abort(
97-
"Found multiple tests with specified description",
95+
"Found multiple tests with description {.str {desc}}.",
9896
call = error_call
9997
)
10098
}
10199

102-
keep_idx <- intersect(seq_along(exprs), c(matching_idx, code_idx))
103-
exprs[[matching_idx]] <- filter_subtests(
104-
exprs[[matching_idx]],
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]],
105105
descs[-1],
106106
error_call = error_call
107107
)
@@ -112,7 +112,6 @@ is_subtest <- function(expr) {
112112
is_call(expr, c("test_that", "describe", "it"), n = 2) && is_string(expr[[2]])
113113
}
114114

115-
116115
#' @rdname source_file
117116
#' @export
118117
source_dir <- function(

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/_snaps/source.new.md

Lines changed: 0 additions & 18 deletions
This file was deleted.

tests/testthat/test-source.R

Lines changed: 46 additions & 83 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
)
96-
expect_equal(filter_subtests(code, "foo"), code[c(1, 2)])
97-
expect_equal(filter_subtests(code, "bar"), code[c(1, 3, 4)])
98-
expect_snapshot(filter_subtests(code, "baz"), error = TRUE)
105+
expect_equal(filter_desc(code, "foo"), code[c(1, 2)])
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()", {
@@ -145,76 +181,3 @@ test_that("source_dir()", {
145181
)
146182
expect_equal(res[[1]](), "Hello World")
147183
})
148-
149-
test_that("you can select deeply nested describe(...)", {
150-
code <- exprs(
151-
f(),
152-
describe("level 0", {
153-
g()
154-
describe("level 1 A", {
155-
h()
156-
describe("level 2 A", {
157-
i()
158-
it("level 3 A", {
159-
expect_equal(1, 1)
160-
})
161-
j()
162-
})
163-
k()
164-
describe("level 2 B", {
165-
l()
166-
it("level 3 B", {
167-
o()
168-
expect_equal(1, 1)
169-
p()
170-
})
171-
m()
172-
it("level 3 C", {
173-
o()
174-
expect_equal(1, 1)
175-
p()
176-
})
177-
n()
178-
it("level 3 D", {
179-
expect_equal(1, 1)
180-
})
181-
o()
182-
})
183-
p()
184-
describe("level 2 C", {
185-
expect_equal(1, 1)
186-
})
187-
r()
188-
})
189-
s()
190-
describe("level 1 B", {})
191-
t()
192-
}),
193-
x()
194-
)
195-
196-
expected <- exprs(
197-
f(),
198-
describe("level 0", {
199-
g()
200-
describe("level 1 A", {
201-
h()
202-
k()
203-
describe("level 2 B", {
204-
l()
205-
m()
206-
it("level 3 C", {
207-
o()
208-
expect_equal(1, 1)
209-
p()
210-
})
211-
})
212-
})
213-
})
214-
)
215-
216-
expect_equal(
217-
filter_subtests(code, c("level 0", "level 1 A", "level 2 B", "level 3 C")),
218-
expected
219-
)
220-
})

0 commit comments

Comments
 (0)