Skip to content

Commit ae5dda6

Browse files
authored
Implement expect_disjoint() (#2239)
Fixes #1851
1 parent 558c788 commit ae5dda6

File tree

6 files changed

+87
-2
lines changed

6 files changed

+87
-2
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export(expect_all_true)
8585
export(expect_condition)
8686
export(expect_contains)
8787
export(expect_cpp_tests_pass)
88+
export(expect_disjoint)
8889
export(expect_equal)
8990
export(expect_equal_to_reference)
9091
export(expect_equivalent)

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+
* New `expect_disjoint()` to check for the absence of values (#1851).
34
* `expect_all_equal()`, `expect_all_true()`, and `expect_all_false()` are a new family of expectations that checks that every element of a vector has the same value. Compared to using `expect_true(all(...))` they give better failure messages (#1836, #2235).
45
* Expectations now consistently return the value of the first argument, regardless of whether the expectation succeeds or fails. The primary exception are `expect_message()` and friends which will return the condition. This shouldn't affect existing tests, but will make failures clearer when you chain together multiple expectations (#2246).
56
* `set_state_inspector()` gains `tolerance` argument and ignores minor FP differences by default (@mcol, #2237).

R/expect-setequal.R

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
#' and that every element of `y` occurs in `x`.
55
#' * `expect_contains(x, y)` tests that `x` contains every element of `y`
66
#' (i.e. `y` is a subset of `x`).
7-
#' * `expect_in(x, y)` tests every element of `x` is in `y`
7+
#' * `expect_in(x, y)` tests that every element of `x` is in `y`
88
#' (i.e. `x` is a subset of `y`).
9+
#' * `expect_disjoint(x, y)` tests that no element of `x` is in `y`
10+
#' (i.e. `x` is disjoint from `y`).
911
#' * `expect_mapequal(x, y)` treats lists as if they are mappings between names
1012
#' and values. Concretely, checks that `x` and `y` have the same names, then
1113
#' checks that `x[names(y)]` equals `y`.
@@ -145,6 +147,7 @@ expect_contains <- function(object, expected) {
145147
invisible(act$val)
146148
}
147149

150+
148151
#' @export
149152
#' @rdname expect_setequal
150153
expect_in <- function(object, expected) {
@@ -174,6 +177,30 @@ expect_in <- function(object, expected) {
174177
invisible(act$val)
175178
}
176179

180+
#' @export
181+
#' @rdname expect_setequal
182+
expect_disjoint <- function(object, expected) {
183+
act <- quasi_label(enquo(object))
184+
exp <- quasi_label(enquo(expected))
185+
186+
check_vector(act$val, error_arg = "object")
187+
check_vector(exp$val, error_arg = "expected")
188+
189+
act_common <- act$val %in% exp$val
190+
if (any(act_common)) {
191+
fail(c(
192+
sprintf("Expected %s to be disjoint from %s.", act$lab, exp$lab),
193+
sprintf("Actual: %s", values(act$val)),
194+
sprintf("Expected: None of %s", values(exp$val)),
195+
sprintf("Invalid: %s", values(act$val[act_common]))
196+
))
197+
} else {
198+
pass()
199+
}
200+
201+
invisible(act$val)
202+
}
203+
177204
# Helpers ----------------------------------------------------------------------
178205

179206
check_vector <- function(x, error_arg, error_call = caller_env()) {

man/expect_setequal.Rd

Lines changed: 6 additions & 1 deletion
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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,36 @@
231231
Expected: "d", "e"
232232
Invalid: "a", "b"
233233

234+
# expect_disjoint() gives useful message on failure
235+
236+
Code
237+
expect_disjoint(x1, x2)
238+
Condition
239+
Error:
240+
! Expected `x1` to be disjoint from `x2`.
241+
Actual: "a", "b", "c"
242+
Expected: None of "c", "d"
243+
Invalid: "c"
244+
245+
---
246+
247+
Code
248+
expect_disjoint(x1, x3)
249+
Condition
250+
Error:
251+
! Expected `x1` to be disjoint from `x3`.
252+
Actual: "a", "b", "c"
253+
Expected: None of "b", "c", "d"
254+
Invalid: "b", "c"
255+
256+
---
257+
258+
Code
259+
expect_disjoint(NA, c("a", NA))
260+
Condition
261+
Error:
262+
! Expected NA to be disjoint from `c("a", NA)`.
263+
Actual: NA
264+
Expected: None of "a", NA
265+
Invalid: NA
266+

tests/testthat/test-expect-setequal.R

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,21 @@ test_that("expect_in() gives useful message on failure", {
154154
expect_snapshot_failure(expect_in(x1, x2))
155155
expect_snapshot_failure(expect_in(x1, x3))
156156
})
157+
158+
# disjoint ----------------------------------------------------------------
159+
160+
test_that("expect_disjoint() succeeds when appropriate", {
161+
expect_success(expect_disjoint(1, letters))
162+
expect_success(expect_disjoint(LETTERS, letters))
163+
expect_success(expect_disjoint(character(), letters))
164+
})
165+
166+
test_that("expect_disjoint() gives useful message on failure", {
167+
x1 <- c("a", "b", "c")
168+
x2 <- c("c", "d")
169+
x3 <- c("b", "c", "d")
170+
171+
expect_snapshot_failure(expect_disjoint(x1, x2))
172+
expect_snapshot_failure(expect_disjoint(x1, x3))
173+
expect_snapshot_failure(expect_disjoint(NA, c("a", NA)))
174+
})

0 commit comments

Comments
 (0)