Skip to content

Commit 03d741b

Browse files
authored
Implement expect_contains() and expect_in() (#1817)
Fixes #1346
1 parent b8b0fe2 commit 03d741b

File tree

6 files changed

+135
-0
lines changed

6 files changed

+135
-0
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export(evaluate_promise)
7979
export(exp_signal)
8080
export(expect)
8181
export(expect_condition)
82+
export(expect_contains)
8283
export(expect_cpp_tests_pass)
8384
export(expect_equal)
8485
export(expect_equal_to_reference)
@@ -89,6 +90,7 @@ export(expect_false)
8990
export(expect_gt)
9091
export(expect_gte)
9192
export(expect_identical)
93+
export(expect_in)
9294
export(expect_invisible)
9395
export(expect_is)
9496
export(expect_known_hash)

NEWS.md

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

3+
* New `expect_contains()` and `expect_in()` that works similarly to
4+
`expect_true(all(expected %in% object))` or
5+
`expect_true(all(object %in% expected))` but give more informative failure
6+
messages (#1346).
7+
38
* Skips are now only shown at the end of reporter summaries, not as tests are
49
run. This makes them less intrusive in interactive tests while still allowing
510
you to verify that the correct tests are skipped (#1801).

R/expect-setequal.R

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
#'
33
#' * `expect_setequal(x, y)` tests that every element of `x` occurs in `y`,
44
#' and that every element of `y` occurs in `x`.
5+
#' * `expect_contains(x, y)` tests that `x` contains every element of `y`
6+
#' (i.e. `y` is a subset of `x`).
7+
#' * `expect_in(x, y)` tests every element of `x` is in `y`
8+
#' (i.e. `x` is a subset of `y`).
59
#' * `expect_mapequal(x, y)` tests that `x` and `y` have the same names, and
610
#' that `x[names(y)]` equals `y`.
711
#'
@@ -117,3 +121,53 @@ check_names_ok <- function(x, label) {
117121
stop("All elements in `", label, "` must be named")
118122
}
119123
}
124+
125+
#' @export
126+
#' @rdname expect_setequal
127+
expect_contains <- function(object, expected) {
128+
act <- quasi_label(enquo(object), arg = "object")
129+
exp <- quasi_label(enquo(expected), arg = "expected")
130+
131+
if (!is_vector(act$val) || !is_vector(exp$val)) {
132+
abort("`object` and `expected` must both be vectors")
133+
}
134+
135+
exp_miss <- !exp$val %in% act$val
136+
137+
if (any(exp_miss)) {
138+
fail(paste0(
139+
act$lab, " (`actual`) doesn't fully contain all the values in ", exp$lab, " (`expected`).\n",
140+
paste0("* Missing from `actual`: ", values(exp$val[exp_miss]), "\n"),
141+
paste0("* Present in `actual`: ", values(act$val), "\n")
142+
))
143+
} else {
144+
succeed()
145+
}
146+
147+
invisible(act$val)
148+
}
149+
150+
#' @export
151+
#' @rdname expect_setequal
152+
expect_in <- function(object, expected) {
153+
act <- quasi_label(enquo(object), arg = "object")
154+
exp <- quasi_label(enquo(expected), arg = "expected")
155+
156+
if (!is_vector(act$val) || !is_vector(exp$val)) {
157+
abort("`object` and `expected` must both be vectors")
158+
}
159+
160+
act_miss <- !act$val %in% exp$val
161+
162+
if (any(act_miss)) {
163+
fail(paste0(
164+
act$lab, " (`actual`) isn't fully contained within ", exp$lab, " (`expected`).\n",
165+
paste0("* Missing from `expected`: ", values(act$val[act_miss]), "\n"),
166+
paste0("* Present in `expected`: ", values(exp$val), "\n")
167+
))
168+
} else {
169+
succeed()
170+
}
171+
172+
invisible(act$val)
173+
}

man/expect_setequal.Rd

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

tests/testthat/_snaps/expect-setequal.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,31 @@
1212
* Only in `expected`: 4, 5, 6, 7, 8, 9, 10, 11, 12, ...
1313

1414

15+
# expect_contains() gives useful message on failure
16+
17+
`x1` (`actual`) doesn't fully contain all the values in `x2` (`expected`).
18+
* Missing from `actual`: "d"
19+
* Present in `actual`: "a", "b", "c"
20+
21+
22+
---
23+
24+
`x1` (`actual`) doesn't fully contain all the values in `x3` (`expected`).
25+
* Missing from `actual`: "d", "e"
26+
* Present in `actual`: "a", "b", "c"
27+
28+
29+
# expect_in() gives useful message on failure
30+
31+
`x1` (`actual`) isn't fully contained within `x2` (`expected`).
32+
* Missing from `expected`: "a"
33+
* Present in `expected`: "b", "c"
34+
35+
36+
---
37+
38+
`x1` (`actual`) isn't fully contained within `x3` (`expected`).
39+
* Missing from `expected`: "a", "b"
40+
* Present in `expected`: "d", "e"
41+
42+

tests/testthat/test-expect-setequal.R

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,39 @@ test_that("succeeds if comparing empty named and unnamed vectors", {
7777
expect_warning(expect_success(expect_mapequal(x2, x1)))
7878
expect_warning(expect_success(expect_mapequal(x2, x2)))
7979
})
80+
81+
# contains ----------------------------------------------------------------
82+
83+
test_that("expect_contains() succeeds when appropriate", {
84+
expect_success(expect_contains(letters, "a"))
85+
expect_success(expect_contains(letters, letters))
86+
expect_success(expect_contains(letters, character()))
87+
})
88+
89+
test_that("expect_contains() gives useful message on failure", {
90+
x1 <- c("a", "b", "c")
91+
x2 <- c("c", "d")
92+
x3 <- c("d", "e")
93+
94+
expect_snapshot_failure(expect_contains(x1, x2))
95+
expect_snapshot_failure(expect_contains(x1, x3))
96+
})
97+
98+
99+
# in ----------------------------------------------------------------
100+
101+
test_that("expect_in() succeeds when appropriate", {
102+
expect_success(expect_in("a", letters))
103+
expect_success(expect_in(letters, letters))
104+
expect_success(expect_in(character(), letters))
105+
})
106+
107+
test_that("expect_in() gives useful message on failure", {
108+
x1 <- c("a", "b")
109+
x2 <- c("b", "c")
110+
x3 <- c("d", "e")
111+
112+
expect_snapshot_failure(expect_in(x1, x2))
113+
expect_snapshot_failure(expect_in(x1, x3))
114+
})
115+

0 commit comments

Comments
 (0)