diff --git a/NEWS.md b/NEWS.md index d1bbe46fb..b6076f438 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # testthat (development version) +* Power `expect_mapequal()` with `waldo::compare(list_as_map = TRUE)` (#1521). * 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.) * `expect_snapshot()` no longer skips on CRAN, as that skips the rest of the test. Instead it just returns, neither succeeding nor failing (#1585). * Interrupting a test now prints the test name. This makes it easier to tell where a very slow test might be hanging (#1464) diff --git a/R/expect-setequal.R b/R/expect-setequal.R index 02f7cb4e8..f13c3ab9b 100644 --- a/R/expect-setequal.R +++ b/R/expect-setequal.R @@ -6,8 +6,9 @@ #' (i.e. `y` is a subset of `x`). #' * `expect_in(x, y)` tests every element of `x` is in `y` #' (i.e. `x` is a subset of `y`). -#' * `expect_mapequal(x, y)` tests that `x` and `y` have the same names, and -#' that `x[names(y)]` equals `y`. +#' * `expect_mapequal(x, y)` treats lists as if they are mappings between names +#' and values. Concretely, this drops `NULL`s in both objects and sorts +#' named components. #' #' Note that `expect_setequal()` ignores names, and you will be warned if both #' `object` and `expected` have them. @@ -77,37 +78,7 @@ expect_mapequal <- function(object, expected) { act <- quasi_label(enquo(object)) exp <- quasi_label(enquo(expected)) - check_vector(object) - check_map_names(object) - check_vector(expected) - check_map_names(expected) - - # Length-0 vectors are OK whether named or unnamed. - if (length(act$val) == 0 && length(exp$val) == 0) { - testthat_warn("`object` and `expected` are empty lists") - return(pass(act$val)) - } - - act_nms <- names(act$val) - exp_nms <- names(exp$val) - if (setequal(act_nms, exp_nms)) { - act <- labelled_value(act$val[exp_nms], act$lab) - return(expect_waldo_equal_("equal", act, exp)) - } - - act_miss <- setdiff(exp_nms, act_nms) - if (length(act_miss) > 0) { - vals <- paste0(encodeString(act_miss, quote = '"'), ", ") - return(fail(paste0("Names absent from `object`: ", vals))) - } - - exp_miss <- setdiff(act_nms, exp_nms) - if (length(exp_miss) > 0) { - vals <- paste0(encodeString(exp_miss, quote = '"'), ", ") - return(fail(paste0("Names absent from `expected`: ", vals))) - } - - pass(act$val) + expect_waldo_equal_("equal", act, exp, list_as_map = TRUE) } #' @export diff --git a/man/expect_setequal.Rd b/man/expect_setequal.Rd index 10660c120..c6b2b40db 100644 --- a/man/expect_setequal.Rd +++ b/man/expect_setequal.Rd @@ -30,8 +30,9 @@ and that every element of \code{y} occurs in \code{x}. (i.e. \code{y} is a subset of \code{x}). \item \code{expect_in(x, y)} tests every element of \code{x} is in \code{y} (i.e. \code{x} is a subset of \code{y}). -\item \code{expect_mapequal(x, y)} tests that \code{x} and \code{y} have the same names, and -that \code{x[names(y)]} equals \code{y}. +\item \code{expect_mapequal(x, y)} treats lists as if they are mappings between names +and values. Concretely, this drops \code{NULL}s in both objects and sorts +named components. } } \details{ diff --git a/tests/testthat/_snaps/expect-setequal.md b/tests/testthat/_snaps/expect-setequal.md index 944d87485..50d57a319 100644 --- a/tests/testthat/_snaps/expect-setequal.md +++ b/tests/testthat/_snaps/expect-setequal.md @@ -50,43 +50,6 @@ * Only in `expected`: 3, 4, 5, 6, 7, 8, 9, 10, 11, ... -# check inputs - - Code - expect_mapequal(sum, named) - Condition - Error in `expect_mapequal()`: - ! `object` must be a vector, not a primitive function. - Code - expect_mapequal(named, sum) - Condition - Error in `expect_mapequal()`: - ! `expected` must be a vector, not a primitive function. - Code - expect_mapequal(unnamed, named) - Condition - Error in `expect_mapequal()`: - ! All elements in `object` must have names. - x Empty names at position: 1 - Code - expect_mapequal(named, unnamed) - Condition - Error in `expect_mapequal()`: - ! All elements in `expected` must have names. - x Empty names at position: 1 - Code - expect_mapequal(named, duplicated) - Condition - Error in `expect_mapequal()`: - ! All elements in `expected` must have unique names. - x Duplicate names: "x" - Code - expect_mapequal(duplicated, named) - Condition - Error in `expect_mapequal()`: - ! All elements in `object` must have unique names. - x Duplicate names: "x" - # expect_contains() gives useful message on failure `x1` (`actual`) doesn't fully contain all the values in `x2` (`expected`). diff --git a/tests/testthat/test-expect-setequal.R b/tests/testthat/test-expect-setequal.R index 0617eabf7..dc87b223d 100644 --- a/tests/testthat/test-expect-setequal.R +++ b/tests/testthat/test-expect-setequal.R @@ -53,10 +53,16 @@ test_that("ignores order", { expect_success(expect_mapequal(list(a = 1, b = 2), list(b = 2, a = 1))) }) +test_that("ignores order recursively", { + x <- list(outer_1 = 1, outer_2 = list(inner_1 = 1, inner_2 = 2)) + y <- list(outer_2 = list(inner_2 = 2, inner_1 = 1), outer_1 = 1) + expect_success(expect_mapequal(x, y)) +}) + test_that("error if any names are duplicated", { - expect_error(expect_mapequal(list(a = 1, b = 2, b = 3), list(b = 2, a = 1))) - expect_error(expect_mapequal(list(a = 1, b = 2), list(b = 3, b = 2, a = 1))) - expect_error(expect_mapequal( + expect_failure(expect_mapequal(list(a = 1, b = 2, b = 3), list(b = 2, a = 1))) + expect_failure(expect_mapequal(list(a = 1, b = 2), list(b = 3, b = 2, a = 1))) + expect_failure(expect_mapequal( list(a = 1, b = 2, b = 3), list(b = 3, b = 2, a = 1) )) @@ -75,31 +81,9 @@ test_that("fails if values don't match", { expect_failure(expect_mapequal(list(a = 1, b = 2), list(a = 1, b = 3))) }) -test_that("check inputs", { - unnamed <- list(1) - named <- list(a = 1) - duplicated <- list(x = 1, x = 2) - - expect_snapshot(error = TRUE, { - expect_mapequal(sum, named) - expect_mapequal(named, sum) - - expect_mapequal(unnamed, named) - expect_mapequal(named, unnamed) - - expect_mapequal(named, duplicated) - expect_mapequal(duplicated, named) - }) -}) - -test_that("succeeds if comparing empty named and unnamed vectors", { - x1 <- list() - x2 <- setNames(list(), character()) - - expect_warning(expect_success(expect_mapequal(x1, x1))) - expect_warning(expect_success(expect_mapequal(x1, x2))) - expect_warning(expect_success(expect_mapequal(x2, x1))) - expect_warning(expect_success(expect_mapequal(x2, x2))) +test_that("fails if unnamed values in different location if any unnamed values", { + expect_success(expect_mapequal(list(1, b = 2, c = 3), list(1, c = 3, b = 2))) + expect_failure(expect_mapequal(list(1, b = 2, c = 3), list(b = 2, 1, c = 3))) }) # contains ----------------------------------------------------------------