diff --git a/CLAUDE.md b/CLAUDE.md index 0852e7cdd..0739e34b8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,18 +12,23 @@ General advice: * When running R from the console, always run it with `--quiet --vanilla` * Always run `air format .` after generating code -### Development tools +### Testing -- `devtools::test()` - Run all tests -- `devtools::test_file("tests/testthat/test-filename.R")` - Run tests in a specific file +- Use `devtools::test()` to run all tests +- Use `devtools::test_file("tests/testthat/test-filename.R")` to run tests in a specific file - DO NOT USE `devtools::test_active_file()` -- `devtools::load_all()` - Load package for development -- `devtools::check()` - Run R CMD check -- `devtools::install()` - Install package locally +- All testing functions automatically load code; you don't needs to. + +- All new code should have an accompanying test. +- Tests for `R/{name}.R` go in `tests/testthat/test-{name}.R`. +- If there are existing tests, place new tests next to similar existing tests. ### Documentation - Always run `devtools::document()` after changing any roxygen2 docs. +- Every user facing function should be exported and have roxygen2 documentation. +- Whenever you add a new documentation file, make sure to also add the topic name to `_pkgdown.yml`. +- Run `pkgdown::check_pkgdown()` to check that all topics are included in the reference index. ## Core Architecture diff --git a/NEWS.md b/NEWS.md index 4989b06a8..b70a51374 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # testthat (development version) +* The failure messages for all `expect_` functions have been rewritten to first state what was expected and then what was actually received (#2142). * `test_file(desc = ...)` no longer loses snapshot results (#2066). * In `R CMD check`, snapshots now only advise on how to resolve failures once (#2207). * `snapshot_review()` includes a reject button and only displays the file navigation and the skip button if there are multiple files to review (#2025). diff --git a/R/expect-comparison.R b/R/expect-comparison.R index 46c6f9a41..27b0915a5 100644 --- a/R/expect-comparison.R +++ b/R/expect-comparison.R @@ -28,14 +28,7 @@ expect_compare_ <- function( operator <- match.arg(operator) op <- match.fun(operator) - msg <- c( - "<" = "not strictly less than", - "<=" = "not less than", - ">" = "not strictly greater than", - ">=" = "not greater than" - )[[operator]] - - negated_op <- switch(operator, "<" = ">=", "<=" = ">", ">" = "<=", ">=" = "<") + actual_op <- switch(operator, "<" = ">=", "<=" = ">", ">" = "<=", ">=" = "<") cmp <- op(act$val, exp$val) if (length(cmp) != 1 || !is.logical(cmp)) { @@ -45,22 +38,32 @@ expect_compare_ <- function( ) } if (!isTRUE(cmp)) { + diff <- act$val - exp$val + msg_exp <- sprintf("Expected %s %s %s.", act$lab, operator, exp$lab) + digits <- max( digits(act$val), digits(exp$val), min_digits(act$val, exp$val) ) - msg <- sprintf( - "%s is %s %s.\n%s - %s = %s %s 0", - act$lab, - msg, - exp$lab, + + msg_act <- sprintf( + "Actual comparison: %s %s %s", num_exact(act$val, digits), - num_exact(exp$val, digits), - num_exact(act$val - exp$val, digits), - negated_op + actual_op, + num_exact(exp$val, digits) ) - return(fail(msg, trace_env = trace_env)) + + if (is.na(diff)) { + msg_diff <- NULL + } else { + msg_diff <- sprintf( + "Difference: %s %s 0", + num_exact(diff, digits), + actual_op + ) + } + return(fail(c(msg_exp, msg_act, msg_diff), trace_env = trace_env)) } pass(act$val) } diff --git a/R/expect-condition.R b/R/expect-condition.R index 449692c32..c7414fead 100644 --- a/R/expect-condition.R +++ b/R/expect-condition.R @@ -440,10 +440,10 @@ compare_condition_3e <- function(cond_type, cond_class, cond, lab, expected) { if (expected) { if (is.null(cond)) { if (is.null(cond_class)) { - sprintf("%s did not throw the expected %s.", lab, cond_type) + sprintf("Expected %s to throw a %s.", lab, cond_type) } else { sprintf( - "%s did not throw a %s with class <%s>.", + "Expected %s to throw a %s with class <%s>.", lab, cond_type, cond_class @@ -454,12 +454,9 @@ compare_condition_3e <- function(cond_type, cond_class, cond, lab, expected) { } } else { if (!is.null(cond)) { - sprintf( - "%s threw an unexpected %s.\nMessage: %s\nClass: %s", - lab, - cond_type, - cnd_message(cond), - paste(class(cond), collapse = "/") + c( + sprintf("Expected %s not to throw any %ss.", lab, cond_type), + actual_condition(cond) ) } else { NULL @@ -493,7 +490,7 @@ compare_condition_2e <- function( # Otherwise we're definitely expecting a condition if (is.null(cond)) { - return(sprintf("%s did not throw an %s.", lab, cond_type)) + return(sprintf("Expected %s to throw a %s.", lab, cond_type)) } matches <- cnd_matches_2e(cond, class, regexp, inherit, ...) @@ -562,7 +559,12 @@ compare_messages <- function( # Expecting no messages if (identical(regexp, NA)) { if (length(messages) > 0) { - return(sprintf("%s generated %s:\n%s", lab, cond_type, bullets)) + return(sprintf( + "Expected %s not to generate %s.\nActually generated:\n%s", + lab, + cond_type, + bullets + )) } else { return() } @@ -570,7 +572,7 @@ compare_messages <- function( # Otherwise we're definitely expecting messages if (length(messages) == 0) { - return(sprintf("%s did not produce any %s.", lab, cond_type)) + return(sprintf("Expected %s to produce %s.", lab, cond_type)) } if (is.null(regexp)) { @@ -625,3 +627,12 @@ check_condition_dots <- function( call = error_call ) } + +actual_condition <- function(cond) { + paste0( + "Actually got a <", + class(cond)[[1]], + "> with message:\n", + indent_lines(cnd_message(cond)) + ) +} diff --git a/R/expect-constant.R b/R/expect-constant.R index 65b0570d5..e6ca95d25 100644 --- a/R/expect-constant.R +++ b/R/expect-constant.R @@ -31,7 +31,7 @@ NULL expect_true <- function(object, info = NULL, label = NULL) { act <- quasi_label(enquo(object), label) exp <- labelled_value(TRUE, "TRUE") - expect_waldo_equal_("equal", act, exp, info = info, ignore_attr = TRUE) + expect_waldo_constant_(act, exp, info = info, ignore_attr = TRUE) } #' @export @@ -39,7 +39,7 @@ expect_true <- function(object, info = NULL, label = NULL) { expect_false <- function(object, info = NULL, label = NULL) { act <- quasi_label(enquo(object), label) exp <- labelled_value(FALSE, "FALSE") - expect_waldo_equal_("equal", act, exp, info = info, ignore_attr = TRUE) + expect_waldo_constant_(act, exp, info = info, ignore_attr = TRUE) } #' Do you expect `NULL`? @@ -58,6 +58,31 @@ expect_false <- function(object, info = NULL, label = NULL) { #' show_failure(expect_null(y)) expect_null <- function(object, info = NULL, label = NULL) { act <- quasi_label(enquo(object), label) - exp <- labelled_value(NULL, "FALSE") - expect_waldo_equal_("equal", act, exp, info = info) + exp <- labelled_value(NULL, "NULL") + expect_waldo_constant_(act, exp, info = info) +} + +expect_waldo_constant_ <- function( + act, + exp, + ..., + info = NULL, + trace_env = caller_env() +) { + comp <- waldo_compare( + act$val, + exp$val, + ..., + x_arg = "actual", + y_arg = "expected" + ) + if (length(comp) != 0) { + msg <- c( + sprintf("Expected %s to be %s.", act$lab, exp$lab), + "Differences:", + paste0(comp, collpase = "\n") + ) + return(fail(msg, info = info, trace_env = trace_env)) + } + pass(act$val) } diff --git a/R/expect-equality.R b/R/expect-equality.R index 08d8bc077..c01665603 100644 --- a/R/expect-equality.R +++ b/R/expect-equality.R @@ -77,13 +77,18 @@ expect_equal <- function( } if (!comp$equal) { - msg <- sprintf("%s not equal to %s.\n%s", act$lab, exp$lab, comp$message) + msg <- c( + sprintf("Expected %s to equal %s.", act$lab, exp$lab), + "Differences:", + comp$message + ) return(fail(msg, info = info)) } pass(act$val) } } + #' @export #' @rdname equality-expectations expect_identical <- function( @@ -102,18 +107,22 @@ expect_identical <- function( } else { ident <- identical(act$val, exp$val, ...) if (ident) { - msg <- "" + msg_act <- NULL } else { compare <- compare(act$val, exp$val) if (compare$equal) { - msg <- "Objects equal but not identical" + msg_act <- "Objects equal but not identical" } else { - msg <- compare$message + msg_act <- compare$message } } if (!ident) { - msg <- sprintf("%s not identical to %s.\n%s", act$lab, exp$lab, msg) + msg <- c( + sprintf("Expected %s to be identical to %s.", act$lab, exp$lab), + "Differences:", + msg_act + ) return(fail(msg, info = info)) } pass(act$val) @@ -126,8 +135,7 @@ expect_waldo_equal_ <- function( exp, info = NULL, ..., - trace_env = caller_env(), - error_prefix = NULL + trace_env = caller_env() ) { comp <- waldo_compare( act$val, @@ -137,16 +145,11 @@ expect_waldo_equal_ <- function( y_arg = "expected" ) if (length(comp) != 0) { - msg <- sprintf( - "%s (%s) is not %s to %s (%s).\n\n%s", - act$lab, - "`actual`", - type, - exp$lab, - "`expected`", - paste0(comp, collapse = "\n\n") + msg <- c( + sprintf("Expected %s to be %s to %s.", act$lab, type, exp$lab), + "Differences:", + paste0(comp, collpase = "\n") ) - msg <- paste0(error_prefix, msg) return(fail(msg, info = info, trace_env = trace_env)) } pass(act$val) @@ -195,7 +198,7 @@ expect_equivalent <- function( comp <- compare(act$val, exp$val, ..., check.attributes = FALSE) if (!comp$equal) { msg <- sprintf( - "%s not equivalent to %s.\n%s", + "Expected %s to be equivalent to %s.\n%s", act$lab, exp$lab, comp$message diff --git a/R/expect-inheritance.R b/R/expect-inheritance.R index 2eff09871..6b55f95c5 100644 --- a/R/expect-inheritance.R +++ b/R/expect-inheritance.R @@ -70,11 +70,9 @@ expect_type <- function(object, type) { act_type <- typeof(act$val) if (!identical(act_type, type)) { - msg <- sprintf( - "%s has type %s, not %s.", - act$lab, - format_class(act_type), - format_class(type) + msg <- c( + sprintf("Expected %s to have type %s.", act$lab, format_class(type)), + sprintf("Actual type: %s", format_class(act_type)) ) return(fail(msg)) } @@ -95,24 +93,29 @@ expect_s3_class <- function(object, class, exact = FALSE) { if (identical(class, NA)) { if (isS3(object)) { - msg <- sprintf("%s is an S3 object", act$lab) + msg <- sprintf("Expected %s not to be an S3 object.", act$lab) return(fail(msg)) } } else if (is.character(class)) { if (!isS3(act$val)) { - return(fail(sprintf("%s is not an S3 object", act$lab))) + msg <- c( + sprintf("Expected %s to be an S3 object.", act$lab), + sprintf("Actual OO type: %s.", oo_type(act$val)) + ) + return(fail(msg)) } else if (exact) { if (!identical(class(act$val), class)) { - msg <- sprintf("%s has class %s, not %s.", act$lab, act$class, exp_lab) + msg <- c( + sprintf("Expected %s to have class %s.", act$lab, exp_lab), + sprintf("Actual class: %s.", act$class) + ) return(fail(msg)) } } else { if (!inherits(act$val, class)) { - msg <- sprintf( - "%s inherits from %s not %s.", - act$lab, - act$class, - exp_lab + msg <- c( + sprintf("Expected %s to inherit from %s.", act$lab, exp_lab), + sprintf("Actual class: %s.", act$class) ) return(fail(msg)) } @@ -133,19 +136,21 @@ expect_s4_class <- function(object, class) { if (identical(class, NA)) { if (isS4(object)) { - msg <- sprintf("%s is an S4 object", act$lab) + msg <- sprintf("Expected %s not to be an S4 object.", act$lab) return(fail(msg)) } } else if (is.character(class)) { if (!isS4(act$val)) { - return(fail(sprintf("%s is not an S4 object", act$lab))) + msg <- c( + sprintf("Expected %s to be an S4 object.", act$lab), + sprintf("Actual OO type: %s.", oo_type(act$val)) + ) + return(fail(msg)) } else { if (!methods::is(act$val, class)) { - msg <- sprintf( - "%s inherits from %s not %s.", - act$lab, - act$class, - exp_lab + msg <- c( + sprintf("Expected %s to inherit from %s.", act$lab, exp_lab), + sprintf("Actual class: %s.", act$class) ) return(fail(msg)) } @@ -164,13 +169,20 @@ expect_r6_class <- function(object, class) { check_string(class) if (!inherits(act$val, "R6")) { - return(fail(sprintf("%s is not an R6 object.", act$lab))) + msg <- c( + sprintf("Expected %s to be an R6 object.", act$lab), + sprintf("Actual OO type: %s.", oo_type(act$val)) + ) + return(fail(msg)) } if (!inherits(act$val, class)) { act_class <- format_class(class(act$val)) exp_class <- format_class(class) - msg <- sprintf("%s inherits from %s not %s.", act$lab, act_class, exp_class) + msg <- c( + sprintf("Expected %s to inherit from %s.", act$lab, exp_class), + sprintf("Actual class: %s.", act_class) + ) return(fail(msg)) } @@ -188,17 +200,21 @@ expect_s7_class <- function(object, class) { act <- quasi_label(enquo(object)) if (!S7::S7_inherits(object)) { - return(fail(sprintf("%s is not an S7 object", act$lab))) + msg <- c( + sprintf("Expected %s to be an S7 object.", act$lab), + sprintf("Actual OO type: %s.", oo_type(act$val)) + ) + return(fail(msg)) } if (!S7::S7_inherits(object, class)) { - obj_class <- setdiff(base::class(object), "S7_object") - class_desc <- paste0("<", obj_class, ">", collapse = "/") - msg <- sprintf( - "%s inherits from %s not <%s>.", - act$lab, - class_desc, - attr(class, "name", TRUE) + exp_class <- attr(class, "name", TRUE) + act_class <- setdiff(base::class(object), "S7_object") + act_class_desc <- paste0("<", act_class, ">", collapse = "/") + + msg <- c( + sprintf("Expected %s to inherit from <%s>.", act$lab, exp_class), + sprintf("Actual class: %s.", act_class_desc) ) return(fail(msg)) } @@ -239,10 +255,10 @@ expect_is <- function(object, class, info = NULL, label = NULL) { if (!inherits(act$val, class)) { msg <- sprintf( - "%s inherits from `%s` not `%s`.", + "Expected %s to inherit from %s.\nActual inheritance: %s", act$lab, - act$class, - exp_lab + exp_lab, + act$class ) return(fail(msg, info = info)) } @@ -254,5 +270,22 @@ expect_is <- function(object, class, info = NULL, label = NULL) { isS3 <- function(x) is.object(x) && !isS4(x) format_class <- function(x) { - paste0(encodeString(x, quote = "'"), collapse = "/") + paste0(encodeString(x, quote = '"'), collapse = "/") +} + +oo_type <- function(x) { + if (!is.object(x)) { + return("none") + } + if (isS4(x)) { + "S4" + } else { + if (inherits(x, "R6")) { + "R6" + } else if (inherits(x, "S7")) { + "S7" + } else { + "S3" + } + } } diff --git a/R/expect-invisible.R b/R/expect-invisible.R index e69a89524..9ae1ec596 100644 --- a/R/expect-invisible.R +++ b/R/expect-invisible.R @@ -25,7 +25,10 @@ expect_invisible <- function(call, label = NULL) { vis <- withVisible(call) if (!identical(vis$visible, FALSE)) { - msg <- sprintf("%s returns visibly, not invisibly.", lab) + msg <- c( + sprintf("Expected %s to return invisibly.", lab), + "Actual visibility: visible." + ) return(fail(msg)) } pass(vis$value) @@ -38,7 +41,10 @@ expect_visible <- function(call, label = NULL) { vis <- withVisible(call) if (!identical(vis$visible, TRUE)) { - msg <- sprintf("%s returns invisibly, not visibly.", lab) + msg <- c( + sprintf("Expected %s to return visibly.", lab), + "Actual visibility: invisible." + ) return(fail(msg)) } pass(vis$value) diff --git a/R/expect-known.R b/R/expect-known.R index db4ea366f..98f32c866 100644 --- a/R/expect-known.R +++ b/R/expect-known.R @@ -235,7 +235,11 @@ expect_known_hash <- function(object, hash = NULL) { cli::cli_warn("No recorded hash: use {substr(act_hash, 1, 10)}.") } else { if (hash != act_hash) { - msg <- sprintf("Value hashes to %s, not %s", act_hash, hash) + msg <- sprintf( + "Expected value to hash to %s.\nActual hash: %s", + hash, + act_hash + ) return(fail(msg)) } } diff --git a/R/expect-match.R b/R/expect-match.R index be5f78eb0..1fc952b47 100644 --- a/R/expect-match.R +++ b/R/expect-match.R @@ -46,7 +46,8 @@ expect_match <- function( check_bool(all) if (length(object) == 0) { - return(fail(sprintf("%s is empty.", act$lab), info = info)) + msg <- sprintf("Expected %s to have at least one element.", act$lab) + return(fail(msg, info = info)) } expect_match_( @@ -106,7 +107,7 @@ expect_match_ <- function( info = NULL, label = NULL, negate = FALSE, - title = "Text", + title = "text", trace_env = caller_env() ) { matches <- grepl(regexp, act$val, perl = perl, fixed = fixed, ...) @@ -117,24 +118,75 @@ expect_match_ <- function( return(pass(act$val)) } + values <- show_text(act$val, condition) if (length(act$val) == 1) { which <- "" } else { - which <- if (all) "Every element of " else "Some element of " + which <- if (all) "every element of " else "some element of " } - match <- if (negate) "matches" else "does not match" + match <- if (negate) "not to match" else "to match" - msg <- sprintf( - "%s%s %s %s %s.\n%s:\n%s", + msg_exp <- sprintf( + "Expected %s%s %s %s %s.", which, act$lab, match, if (fixed) "string" else "regexp", - encodeString(regexp, quote = '"'), - title, - paste0(show_text(act$val, condition), collapse = "\n") + encodeString(regexp, quote = '"') ) - return(fail(msg, info = info, trace_env = trace_env)) + msg_act <- c(paste0("Actual ", title, ':'), values) + return(fail(c(msg_exp, msg_act), info = info, trace_env = trace_env)) +} + + +# Adapted from print.ellmer_prompt +show_text <- function( + x, + condition, + ..., + max_items = 20, + max_lines = max_items * 25 +) { + n <- length(x) + n_extra <- length(x) - max_items + if (n_extra > 0) { + x <- x[seq_len(max_items)] + condition <- condition[seq_len(max_items)] + } + + if (length(x) == 0) { + return(character()) + } + + bar <- if (cli::is_utf8_output()) "\u2502" else "|" + + id <- ifelse( + condition, + cli::col_green(cli::symbol$tick), + cli::col_red(cli::symbol$cross) + ) + + indent <- paste0(id, " ", bar, " ") + exdent <- paste0(" ", cli::col_grey(bar), " ") + + x[is.na(x)] <- cli::col_red("") + x <- paste0(indent, x) + x <- gsub("\n", paste0("\n", exdent), x) + + lines <- strsplit(x, "\n") + ids <- rep(seq_along(x), length(lines)) + lines <- unlist(lines) + + if (length(lines) > max_lines) { + lines <- lines[seq_len(max_lines)] + lines <- c(lines, paste0(exdent, "...")) + n_extra <- n - ids[max_lines - 1] + } + + if (n_extra > 0) { + lines <- c(lines, paste0("... and ", n_extra, " more.\n")) + } + lines } diff --git a/R/expect-named.R b/R/expect-named.R index 6f9e63f1b..4f8cb10c6 100644 --- a/R/expect-named.R +++ b/R/expect-named.R @@ -35,6 +35,7 @@ expect_named <- function( check_bool(ignore.order) check_bool(ignore.case) + act <- quasi_label(enquo(object), label) if (missing(expected)) { @@ -47,11 +48,11 @@ expect_named <- function( act_names <- normalise_names(names(act$val), ignore.order, ignore.case) if (ignore.order) { - act <- labelled_value(act_names, act$lab) - return(expect_setequal_(act, exp, error_prefix = "Names of ")) + act <- labelled_value(act_names, paste0("names(", act$lab, ")")) + return(expect_setequal_(act, exp)) } else { - act <- labelled_value(act_names, act$lab) - return(expect_waldo_equal_("equal", act, exp, error_prefix = "Names of ")) + act <- labelled_value(act_names, paste0("names(", act$lab, ")")) + return(expect_waldo_equal_("equal", act, exp)) } pass(act$val) @@ -75,7 +76,7 @@ normalise_names <- function(x, ignore.order = FALSE, ignore.case = FALSE) { expect_has_names_ <- function(act, trace_env = caller_env()) { act_names <- names(act$val) if (identical(act_names, NULL)) { - msg <- sprintf("%s does not have names.", act$lab) + msg <- sprintf("Expected %s to have names.", act$lab) return(fail(msg, trace_env = trace_env)) } return(pass(act$val)) diff --git a/R/expect-no-condition.R b/R/expect-no-condition.R index bd0376a0b..50b939fb2 100644 --- a/R/expect-no-condition.R +++ b/R/expect-no-condition.R @@ -108,24 +108,18 @@ expect_no_ <- function( act <- quasi_capture(enquo(object), NULL, capture) if (!is.null(first_match)) { - expected <- paste0( + msg_exp <- paste0( "Expected ", - quo_label(enquo(object)), - " to run without any ", + act$lab, + " not to throw any ", base_class, "s", if (!is.null(class)) paste0(" of class '", class, "'"), if (!is.null(regexp)) paste0(" matching pattern '", regexp, "'"), "." ) - actual <- paste0( - "Actually got a <", - class(first_match)[[1]], - "> with text:\n", - indent_lines(rlang::cnd_message(first_match)) - ) - message <- format_error_bullets(c(expected, i = actual)) - return(fail(message, trace_env = trace_env)) + msg_act <- actual_condition(first_match) + return(fail(c(msg_exp, msg_act), trace_env = trace_env)) } pass(act$val) diff --git a/R/expect-output.R b/R/expect-output.R index 65c16e950..fea13a7a4 100644 --- a/R/expect-output.R +++ b/R/expect-output.R @@ -36,18 +36,21 @@ expect_output <- function( if (identical(regexp, NA)) { if (!identical(act$cap, "")) { - msg <- sprintf("%s produced output.\n%s", act$lab, encodeString(act$cap)) + msg <- c( + sprintf("Expected %s to produce no output.", act$lab), + sprintf("Actual output:\n%s", encodeString(act$cap)) + ) return(fail(msg, info = info)) } pass(act$val) } else if (is.null(regexp) || identical(act$cap, "")) { if (identical(act$cap, "")) { - msg <- sprintf("%s produced no output", act$lab) + msg <- sprintf("Expected %s to produce output.", act$lab) return(fail(msg, info = info)) } pass(act$val) } else { act <- labelled_value(act$cap, act$lab) - expect_match_(act, enc2native(regexp), ..., title = "Output") + expect_match_(act, enc2native(regexp), ..., title = "output") } } diff --git a/R/expect-reference.R b/R/expect-reference.R index e325085c4..6f23b3c7d 100644 --- a/R/expect-reference.R +++ b/R/expect-reference.R @@ -27,7 +27,7 @@ expect_reference <- function( exp <- quasi_label(enquo(expected), expected.label) if (!is_reference(act$val, exp$val)) { - msg <- sprintf("%s not a reference to %s.", act$lab, exp$lab) + msg <- sprintf("Expected %s to be a reference to %s.", act$lab, exp$lab) return(fail(msg, info = info)) } pass(act$val) diff --git a/R/expect-self-test.R b/R/expect-self-test.R index bddf26646..05da3bf9f 100644 --- a/R/expect-self-test.R +++ b/R/expect-self-test.R @@ -46,20 +46,20 @@ capture_success_failure <- function(expr) { expect_success <- function(expr) { status <- capture_success_failure(expr) - if (status$n_success == 0) { - return(fail("Expectation did not succeed")) - } else if (status$n_success > 1) { - return(fail(sprintf( - "Expectation succeeded %i times, instead of once", - status$n_success - ))) + if (status$n_success != 1) { + msg <- c( + "Expected one success.", + sprintf("Actually succeeded %i times", status$n_success) + ) + return(fail(msg)) } if (status$n_failure > 0) { - return(fail(sprintf( - "Expectation failed %i times, instead of zero", - status$n_failure - ))) + msg <- c( + "Expected zero failures.", + sprintf("Actually failed %i times", status$n_failure) + ) + return(fail(msg)) } pass(NULL) @@ -70,23 +70,25 @@ expect_success <- function(expr) { expect_failure <- function(expr, message = NULL, ...) { status <- capture_success_failure(expr) - if (status$n_failure == 0) { - return(fail("Expectation did not fail")) - } else if (status$n_failure > 1) { - # This should be impossible, but including for completeness - return(fail("Expectation failed more than once")) + if (status$n_failure != 1) { + msg <- c( + "Expected one failure.", + sprintf("Actually failed %i times", status$n_failure) + ) + return(fail(msg)) } if (status$n_success != 0) { - return(fail(sprintf( - "Expectation succeeded %i times, instead of never", - status$n_success - ))) + msg <- c( + "Expected zero successes.", + sprintf("Actually succeeded %i times", status$n_success) + ) + return(fail(msg)) } if (!is.null(message)) { - act <- labelled_value(status$last_failure$message, "Failure message") - return(expect_match_(act, message, ...)) + act <- labelled_value(status$last_failure$message, "failure message") + return(expect_match_(act, message, ..., title = "message")) } pass(NULL) } @@ -94,7 +96,8 @@ expect_failure <- function(expr, message = NULL, ...) { #' @export #' @rdname expect_success expect_snapshot_failure <- function(expr) { - expect_snapshot_condition_("expectation_failure", expr) + expr <- enquo0(expr) + expect_snapshot_(expr, error = TRUE, error_class = "expectation_failure") } #' Test for absence of success or failure diff --git a/R/expect-setequal.R b/R/expect-setequal.R index 3a39e07be..10b864bf2 100644 --- a/R/expect-setequal.R +++ b/R/expect-setequal.R @@ -40,29 +40,25 @@ expect_setequal <- function(object, expected) { expect_setequal_ <- function( act, exp, - trace_env = caller_env(), - error_prefix = NULL + trace_env = caller_env() ) { act_miss <- unique(act$val[!act$val %in% exp$val]) exp_miss <- unique(exp$val[!exp$val %in% act$val]) if (length(exp_miss) || length(act_miss)) { - msg <- paste0( - if (!is.null(error_prefix)) { - error_prefix - }, + msg_exp <- sprintf( + "Expected %s to have the same values as %s.", act$lab, - " (`actual`) and ", - exp$lab, - " (`expected`) don't have the same values.\n", - if (length(act_miss)) { - paste0("* Only in `actual`: ", values(act_miss), "\n") - }, - if (length(exp_miss)) { - paste0("* Only in `expected`: ", values(exp_miss), "\n") - } + exp$lab ) - return(fail(msg, trace_env = trace_env)) + msg_act <- c( + sprintf("Actual: %s", values(act$val)), + sprintf("Expected: %s", values(exp$val)), + if (length(act_miss)) sprintf("Needs: %s", values(act_miss)), + if (length(exp_miss)) sprintf("Absent: %s", values(exp_miss)) + ) + + return(fail(c(msg_exp, msg_act), trace_env = trace_env)) } pass(act$val) } @@ -105,14 +101,17 @@ expect_contains <- function(object, expected) { exp_miss <- !exp$val %in% act$val if (any(exp_miss)) { - return(fail(paste0( + msg_exp <- sprintf( + "Expected %s to contain all values in %s.", act$lab, - " (`actual`) doesn't fully contain all the values in ", - exp$lab, - " (`expected`).\n", - paste0("* Missing from `actual`: ", values(exp$val[exp_miss]), "\n"), - paste0("* Present in `actual`: ", values(act$val), "\n") - ))) + exp$lab + ) + msg_act <- c( + sprintf("Actual: %s", values(act$val)), + sprintf("Expected: %s", values(exp$val)), + sprintf("Missing: %s", values(exp$val[exp_miss])) + ) + fail(c(msg_exp, msg_act)) } pass(act$val) @@ -129,14 +128,17 @@ expect_in <- function(object, expected) { act_miss <- !act$val %in% exp$val if (any(act_miss)) { - return(fail(paste0( + msg_exp <- sprintf( + "Expected %s to only contain values from %s.", act$lab, - " (`actual`) isn't fully contained within ", - exp$lab, - " (`expected`).\n", - paste0("* Missing from `expected`: ", values(act$val[act_miss]), "\n"), - paste0("* Present in `expected`: ", values(exp$val), "\n") - ))) + exp$lab + ) + msg_act <- c( + sprintf("Actual: %s", values(act$val)), + sprintf("Expected: %s", values(exp$val)), + sprintf("Invalid: %s", values(act$val[act_miss])) + ) + fail(c(msg_exp, msg_act)) } pass(act$val) diff --git a/R/expect-shape.R b/R/expect-shape.R index 964230fd5..c7c1ae311 100644 --- a/R/expect-shape.R +++ b/R/expect-shape.R @@ -28,7 +28,10 @@ expect_length <- function(object, n) { act$n <- length(act$val) if (act$n != n) { - msg <- sprintf("%s has length %i, not length %i.", act$lab, act$n, n) + msg <- c( + sprintf("Expected %s to have length %i.", act$lab, n), + sprintf("Actual length: %i.", act$n) + ) return(fail(msg)) } pass(act$val) @@ -46,7 +49,7 @@ expect_shape = function(object, ..., nrow, ncol, dim) { dim_object <- base::dim(object) if (is.null(dim_object)) { - return(fail(sprintf("%s has no dimensions.", act$lab))) + return(fail(sprintf("Expected %s to have dimensions.", act$lab))) } if (!missing(nrow)) { @@ -54,20 +57,27 @@ expect_shape = function(object, ..., nrow, ncol, dim) { act$nrow <- dim_object[1L] if (!identical(as.integer(act$nrow), as.integer(nrow))) { - msg <- sprintf("%s has %i rows, not %i.", act$lab, act$nrow, nrow) + msg <- c( + sprintf("Expected %s to have %i rows.", act$lab, nrow), + sprintf("Actual rows: %i.", act$nrow) + ) return(fail(msg)) } } else if (!missing(ncol)) { check_number_whole(ncol, allow_na = TRUE) if (length(dim_object) == 1L) { - return(fail(sprintf("%s has only one dimension.", act$lab))) + msg <- sprintf("Expected %s to have two or more dimensions.", act$lab) + return(fail(msg)) } act$ncol <- dim_object[2L] if (!identical(as.integer(act$ncol), as.integer(ncol))) { - msg <- sprintf("%s has %i columns, not %i.", act$lab, act$ncol, ncol) + msg <- c( + sprintf("Expected %s to have %i columns.", act$lab, ncol), + sprintf("Actual columns: %i.", act$ncol) + ) return(fail(msg)) } } else { @@ -78,20 +88,16 @@ expect_shape = function(object, ..., nrow, ncol, dim) { act$dim <- dim_object if (length(act$dim) != length(dim)) { - return(fail(sprintf( - "%s has %i dimensions, not %i.", - act$lab, - length(act$dim), - length(dim) - ))) + msg <- c( + sprintf("Expected %s to have %i dimensions.", act$lab, length(dim)), + sprintf("Actual dimensions: %i.", length(act$dim)) + ) } if (!identical(as.integer(act$dim), as.integer(dim))) { - msg <- sprintf( - "%s has dim (%s), not (%s).", - act$lab, - toString(act$dim), - toString(dim) + msg <- c( + sprintf("Expected %s to have dim (%s).", act$lab, toString(dim)), + sprintf("Actual dim: (%s).", toString(act$dim)) ) return(fail(msg)) } diff --git a/R/expect-silent.R b/R/expect-silent.R index 0d409cf96..7d96c5fec 100644 --- a/R/expect-silent.R +++ b/R/expect-silent.R @@ -27,7 +27,10 @@ expect_silent <- function(object) { ) if (length(outputs) != 0) { - msg <- sprintf("%s produced %s.", act$lab, paste(outputs, collapse = ", ")) + msg <- c( + sprintf("Expected %s to run silently.", act$lab), + sprintf("Actual noise: %s.", paste(outputs, collapse = ", ")) + ) return(fail(msg)) } pass(act$cap$result) diff --git a/R/quasi-label.R b/R/quasi-label.R index 1d6bfc545..0390da8b4 100644 --- a/R/quasi-label.R +++ b/R/quasi-label.R @@ -52,11 +52,10 @@ quasi_label <- function(quo, label = NULL, arg = NULL) { } expr <- quo_get_expr(quo) + value <- eval_bare(expr, quo_get_env(quo)) + label <- label %||% expr_label(expr) - labelled_value( - eval_bare(expr, quo_get_env(quo)), - label %||% expr_label(expr) - ) + labelled_value(value, label) } labelled_value <- function(value, label) { @@ -101,7 +100,7 @@ expr_label <- function(x) { x <- call2(x[[1]], quote(expr = ...)) } } - deparse1(x) + paste0("`", deparse1(x), "`") } else { # Any other object that's been inlined in x <- deparse(x) diff --git a/R/snapshot.R b/R/snapshot.R index a53b77e42..952673bcf 100644 --- a/R/snapshot.R +++ b/R/snapshot.R @@ -68,18 +68,38 @@ expect_snapshot <- function( variant = NULL, cnd_class = FALSE ) { - check_bool(cran) - check_bool(error) - check_bool(cnd_class) - edition_require(3, "expect_snapshot()") + + x <- enquo0(x) + expect_snapshot_( + x, + cran = cran, + error = error, + transform = transform, + variant = variant, + cnd_class = cnd_class + ) +} + +expect_snapshot_ <- function( + x, + cran = TRUE, + error = FALSE, + error_class = NULL, + transform = NULL, + variant = NULL, + cnd_class = FALSE, + error_frame = caller_env() +) { + check_bool(cran, call = error_frame) + check_bool(error, call = error_frame) + check_bool(cnd_class, call = error_frame) + variant <- check_variant(variant) if (!is.null(transform)) { transform <- as_function(transform) } - x <- enquo0(x) - # Execute code, capturing last error state <- new_environment(list(error = NULL)) replay <- function(x) { @@ -95,7 +115,13 @@ expect_snapshot <- function( ) # Use expect_error() machinery to confirm that error is as expected - msg <- compare_condition_3e("error", NULL, state$error, quo_label(x), error) + msg <- compare_condition_3e( + cond_type = "error", + cond_class = error_class, + cond = state$error, + lab = quo_label(x), + expected = error + ) if (!is.null(msg)) { if (error) { return(fail(msg, trace = state$error[["trace"]])) @@ -112,7 +138,7 @@ expect_snapshot <- function( save = function(x) paste0(x, collapse = "\n"), load = function(x) split_by_line(x)[[1]], variant = variant, - trace_env = caller_env() + trace_env = error_frame ) } diff --git a/tests/testthat/_snaps/expect-comparison.md b/tests/testthat/_snaps/expect-comparison.md index e204dd779..30c210eab 100644 --- a/tests/testthat/_snaps/expect-comparison.md +++ b/tests/testthat/_snaps/expect-comparison.md @@ -1,22 +1,80 @@ +# basic comparisons work + + Code + expect_lt(x, 10) + Condition + Error: + ! Expected `x` < 10. + Actual comparison: 10.0 >= 10.0 + Difference: 0.0 >= 0 + +--- + + Code + expect_gt(x, 10) + Condition + Error: + ! Expected `x` > 10. + Actual comparison: 10.0 <= 10.0 + Difference: 0.0 <= 0 + # useful output when numbers are very small - 1.1 * x is not less than `x`. - 0.0000110 - 0.0000100 = 0.0000010 > 0 + Code + expect_lte(1.1 * x, x) + Condition + Error: + ! Expected `1.1 * x` <= `x`. + Actual comparison: 0.0000110 > 0.0000100 + Difference: 0.0000010 > 0 --- - `x` is not strictly greater than 1.1 * x. - 0.0000100 - 0.0000110 = -0.0000010 <= 0 + Code + expect_gt(x, 1.1 * x) + Condition + Error: + ! Expected `x` > `1.1 * x`. + Actual comparison: 0.0000100 <= 0.0000110 + Difference: -0.0000010 <= 0 # useful output when difference is zero - `x` is not strictly less than 100. - 100.0 - 100.0 = 0.0 >= 0 + Code + expect_lt(x, 100) + Condition + Error: + ! Expected `x` < 100. + Actual comparison: 100.0 >= 100.0 + Difference: 0.0 >= 0 # useful output when differnce is large - `x` is not strictly less than 0.001. - 100.000 - 0.001 = 99.999 >= 0 + Code + expect_lt(x, 0.001) + Condition + Error: + ! Expected `x` < 0.001. + Actual comparison: 100.000 >= 0.001 + Difference: 99.999 >= 0 + +# comparisons with Inf work + + Code + expect_lt(x, Inf) + Condition + Error: + ! Expected `x` < Inf. + Actual comparison: Inf >= Inf + +# comparisons with NA work + + Code + expect_lt(x, 10) + Condition + Error: + ! Expected `x` < 10. + Actual comparison: NA >= 10.0 # comparison must yield a single logical diff --git a/tests/testthat/_snaps/expect-condition.md b/tests/testthat/_snaps/expect-condition.md index ae4a570f0..f2b5a7e97 100644 --- a/tests/testthat/_snaps/expect-condition.md +++ b/tests/testthat/_snaps/expect-condition.md @@ -1,10 +1,28 @@ # regexp = NULL checks for presence of error - `{ ... }` did not throw the expected error. + Code + expect_error(f()) + Condition + Error: + ! Expected `f()` to throw a error. + +# regexp = NA checks for absence of error + + Code + expect_error(f(), NA) + Condition + Error: + ! Expected `f()` not to throw any errors. + Actually got a with message: + Yes # regexp = string matches for error message - "OK" did not throw the expected error. + Code + expect_error(f(), "No") + Condition + Error: + ! Expected `f()` to throw a error. # expect_error validates its inputs @@ -26,9 +44,13 @@ # message method is called when expecting error - `fb()` threw an unexpected error. - Message: dispatched! - Class: foobar/rlang_error/error/condition + Code + expect_error(fb(), NA) + Condition + Error: + ! Expected `fb()` not to throw any errors. + Actually got a with message: + dispatched! # expect_warning validates its inputs @@ -53,6 +75,17 @@ Error in `expect_warning()`: ! `all` must be `TRUE` or `FALSE`, not the string "yes". +# regexp = NA checks for absence of message + + Code + expect_message(f(), NA) + Condition + Error: + ! Expected `f()` not to throw any messages. + Actually got a with message: + ! + + # expect_message validates its inputs Code @@ -78,7 +111,11 @@ # condition class is included in failure - `f1()` did not throw a condition with class . + Code + expect_condition(f1(), class = "bar") + Condition + Error: + ! Expected `f1()` to throw a condition with class . # expect_condition validates its inputs diff --git a/tests/testthat/_snaps/expect-constant.md b/tests/testthat/_snaps/expect-constant.md index 2347c401a..37dcbd7f9 100644 --- a/tests/testthat/_snaps/expect-constant.md +++ b/tests/testthat/_snaps/expect-constant.md @@ -1,35 +1,33 @@ # logical tests act as expected - FALSE (`actual`) is not equal to TRUE (`expected`). - - `actual`: FALSE - `expected`: TRUE + Code + expect_true(df) + Condition + Error: + ! Expected `df` to be TRUE. + Differences: + `actual` is an S3 object of class , a list + `expected` is a logical vector (TRUE) --- - TRUE (`actual`) is not equal to FALSE (`expected`). - - `actual`: TRUE - `expected`: FALSE - -# can compare non-vectors - - quote(x) (`actual`) is not equal to TRUE (`expected`). - - `actual` is a symbol - `expected` is a logical vector (TRUE) + Code + expect_false(df) + Condition + Error: + ! Expected `df` to be FALSE. + Differences: + `actual` is an S3 object of class , a list + `expected` is a logical vector (FALSE) # expect_null works - 1L (`actual`) is not equal to FALSE (`expected`). - - `actual` is an integer vector (1) - `expected` is NULL - ---- - - environment() (`actual`) is not equal to FALSE (`expected`). - - `actual` is an environment - `expected` is NULL + Code + expect_null(df) + Condition + Error: + ! Expected `df` to be NULL. + Differences: + `actual` is an S3 object of class , a list + `expected` is NULL diff --git a/tests/testthat/_snaps/expect-equality.md b/tests/testthat/_snaps/expect-equality.md index 62ad9f0e8..714b3cecd 100644 --- a/tests/testthat/_snaps/expect-equality.md +++ b/tests/testthat/_snaps/expect-equality.md @@ -1,26 +1,98 @@ -# provide useful feedback on failure +# provide useful feedback on failure (3e) - 1 (`actual`) is not identical to "a" (`expected`). - - `actual` is a double vector (1) - `expected` is a character vector ('a') + Code + expect_identical(x, "a") + Condition + Error: + ! Expected `x` to be identical to "a". + Differences: + `actual` is a double vector (1) + `expected` is a character vector ('a') --- - 1 (`actual`) is not equal to "a" (`expected`). - - `actual` is a double vector (1) - `expected` is a character vector ('a') + Code + expect_equal(x, "a") + Condition + Error: + ! Expected `x` to be equal to "a". + Differences: + `actual` is a double vector (1) + `expected` is a character vector ('a') --- - 1 not identical to "a". - Types not compatible: double is not character + Code + expect_identical(x, "a") + Condition + Error: + ! Expected `x` to be identical to "a". + Differences: + Types not compatible: double is not character + +--- + + Code + expect_equal(x, "a") + Condition + Error: + ! Expected `x` to equal "a". + Differences: + Types not compatible: double is not character + +# provide useful feedback on failure (2e) + + Code + expect_identical(x, "a") + Condition + Error: + ! Expected `x` to be identical to "a". + Differences: + Types not compatible: double is not character --- - 1 not equal to "a". - Types not compatible: double is not character + Code + expect_equal(x, "a") + Condition + Error: + ! Expected `x` to equal "a". + Differences: + Types not compatible: double is not character + +# default labels use unquoting + + Code + expect_equal(x, !!y) + Condition + Error: + ! Expected `x` to be equal to 2. + Differences: + `actual`: 1.0 + `expected`: 2.0 + +# useful message if objects equal but not identical + + Code + expect_identical(f, g) + Condition + Error: + ! Expected `f` to be identical to `g`. + Differences: + Objects equal but not identical + +# attributes for object (#452) + + Code + expect_equal(oops, 0) + Condition + Error: + ! Expected `oops` to equal 0. + Differences: + Attributes: < Modes: list, NULL > + Attributes: < Lengths: 1, 0 > + Attributes: < names for target but not for current > + Attributes: < current is not list-like > # expect_equal validates its inputs diff --git a/tests/testthat/_snaps/expect-inheritance.md b/tests/testthat/_snaps/expect-inheritance.md index 3df13de95..aa4342115 100644 --- a/tests/testthat/_snaps/expect-inheritance.md +++ b/tests/testthat/_snaps/expect-inheritance.md @@ -1,3 +1,12 @@ +# expect_type checks typeof + + Code + expect_type(x, "double") + Condition + Error: + ! Expected `x` to have type "double". + Actual type: "integer" + # expect_type validates its inputs Code @@ -6,17 +15,66 @@ Error in `expect_type()`: ! `type` must be a single string, not a character vector. +# expect_is checks class + + Code + expect_is(factor("a"), "integer") + Condition + Error: + ! Expected `factor("a")` to inherit from "character". + Actual inheritance: "factor" + +# expect_s3/s4_class fails if appropriate type + + Code + expect_s3_class(x1, "double") + Condition + Error: + ! Expected `x1` to be an S3 object. + Actual OO type: none. + +--- + + Code + expect_s3_class(x2, "double") + Condition + Error: + ! Expected `x2` to be an S3 object. + Actual OO type: S4. + +--- + + Code + expect_s4_class(x3, "double") + Condition + Error: + ! Expected `x3` to be an S4 object. + Actual OO type: S3. + # expect_s[34]_class can check not S3/S4 - factor() is an S3 object + Code + expect_s3_class(factor(), NA) + Condition + Error: + ! Expected `factor()` not to be an S3 object. --- - A() is an S4 object + Code + expect_s4_class(A(), NA) + Condition + Error: + ! Expected `A()` not to be an S4 object. # test_s4_class respects class hierarchy - C() inherits from 'C'/'A'/'B'/'list'/'vector' not 'D'. + Code + expect_s4_class(C(), "D") + Condition + Error: + ! Expected `C()` to inherit from "D". + Actual class: "C"/"A"/"B"/"list"/"vector". # expect_s3_class validates its inputs @@ -33,11 +91,30 @@ # test_s3_class respects class hierarchy - `x` inherits from 'a'/'b' not 'c'. + Code + expect_s3_class(x, "c") + Condition + Error: + ! Expected `x` to inherit from "c". + Actual class: "a"/"b". --- - `x` inherits from 'a'/'b' not 'c'/'d'. + Code + expect_s3_class(x, c("c", "d")) + Condition + Error: + ! Expected `x` to inherit from "c"/"d". + Actual class: "a"/"b". + +# test_s3_class can request exact match + + Code + expect_s3_class(x, "a", exact = TRUE) + Condition + Error: + ! Expected `x` to have class "a". + Actual class: "a"/"b". # expect_s4_class validates its inputs @@ -49,7 +126,18 @@ # expect_r6_class generates useful failures - `x` is not an R6 object. + Code + expect_r6_class(x, "Student") + Condition + Error: + ! Expected `x` to be an R6 object. + Actual OO type: none. + Code + expect_r6_class(person, "Student") + Condition + Error: + ! Expected `person` to inherit from "Student". + Actual class: "Person"/"R6". # expect_r6_class validates its inputs @@ -61,11 +149,30 @@ # can check with actual class - Foo() inherits from not . + Code + expect_s7_class(Foo(), class = Bar) + Condition + Error: + ! Expected `Foo()` to inherit from . + Actual class: . --- - Baz() inherits from / not . + Code + expect_s7_class(Baz(), class = Bar) + Condition + Error: + ! Expected `Baz()` to inherit from . + Actual class: /. + +# informative failure if not S7 + + Code + expect_s7_class(x, Foo) + Condition + Error: + ! Expected `x` to be an S7 object. + Actual OO type: S3. # expect_s7_class validates its inputs diff --git a/tests/testthat/_snaps/expect-invisible.md b/tests/testthat/_snaps/expect-invisible.md index bdd7b69fc..ed32db00f 100644 --- a/tests/testthat/_snaps/expect-invisible.md +++ b/tests/testthat/_snaps/expect-invisible.md @@ -1,8 +1,36 @@ +# basic principles of visibility hold + + Code + expect_invisible(x) + Condition + Error: + ! Expected `x` to return invisibly. + Actual visibility: visible. + +--- + + Code + expect_visible(x <- 1) + Condition + Error: + ! Expected `x <- 1` to return visibly. + Actual visibility: invisible. + # generates useful failure messages - invisible(1) returns invisibly, not visibly. + Code + expect_visible(invisible(1)) + Condition + Error: + ! Expected `invisible(1)` to return visibly. + Actual visibility: invisible. --- - 1 returns visibly, not invisibly. + Code + expect_invisible(1) + Condition + Error: + ! Expected 1 to return invisibly. + Actual visibility: visible. diff --git a/tests/testthat/_snaps/expect-match.md b/tests/testthat/_snaps/expect-match.md index 6ac634185..c8e9396e1 100644 --- a/tests/testthat/_snaps/expect-match.md +++ b/tests/testthat/_snaps/expect-match.md @@ -1,43 +1,77 @@ -# generates useful failure messages +# useful failure if empty - `zero` is empty. + Code + expect_match(zero, "asdf") + Condition + Error: + ! Expected `zero` to have at least one element. ---- +# useful failure messages for scalars - `one` does not match regexp "asdf". - Text: - ✖ │ bcde + Code + expect_match(one, "asdf") + Condition + Error: + ! Expected `one` to match regexp "asdf". + Actual text: + ✖ │ bcde --- - Every element of `many` does not match regexp "a". - Text: - ✔ │ a - ✔ │ a - ✖ │ b + Code + expect_match(one, "asdf", fixed = TRUE) + Condition + Error: + ! Expected `one` to match string "asdf". + Actual text: + ✖ │ bcde + +# useful failure messages for vectors + + Code + expect_match(many, "a") + Condition + Error: + ! Expected every element of `many` to match regexp "a". + Actual text: + ✔ │ a + ✔ │ a + ✖ │ b --- - Some element of `many` does not match regexp "c". - Text: - ✖ │ a - ✖ │ a - ✖ │ b + Code + expect_match(many, "c", all = FALSE) + Condition + Error: + ! Expected some element of `many` to match regexp "c". + Actual text: + ✖ │ a + ✖ │ a + ✖ │ b --- - Every element of `paragraph` does not match regexp "paragraph". - Text: - ✔ │ This is a multiline - │ paragraph. - ✖ │ Second element. + Code + expect_match(paragraph, "paragraph") + Condition + Error: + ! Expected every element of `paragraph` to match regexp "paragraph". + Actual text: + ✔ │ This is a multiline + │ paragraph. + ✖ │ Second element. --- - Every element of `na` does not match regexp "NA". - Text: - ✔ │ NA - ✖ │ + Code + expect_match(na, "NA") + Condition + Error: + ! Expected every element of `na` to match regexp "NA". + Actual text: + ✔ │ NA + ✖ │ # expect_match validates its inputs @@ -97,13 +131,21 @@ # expect_no_match works - `x` matches string "e*". - Text: - x | te*st + Code + expect_no_match(x, "e*", fixed = TRUE) + Condition + Error: + ! Expected `x` not to match string "e*". + Actual text: + x | te*st --- - `x` matches regexp "TEST". - Text: - x | test + Code + expect_no_match(x, "TEST", ignore.case = TRUE) + Condition + Error: + ! Expected `x` not to match regexp "TEST". + Actual text: + x | test diff --git a/tests/testthat/_snaps/expect-named.md b/tests/testthat/_snaps/expect-named.md index 3a54b0219..1cd4f1e45 100644 --- a/tests/testthat/_snaps/expect-named.md +++ b/tests/testthat/_snaps/expect-named.md @@ -1,42 +1,88 @@ +# expected_named verifies presence of names + + Code + expect_named(x) + Condition + Error: + ! Expected `x` to have names. + +# expected_named verifies actual of names + + Code + expect_named(x, "b") + Condition + Error: + ! Expected names(`x`) to be equal to "b". + Differences: + `actual`: "a" + `expected`: "b" + # provide useful feedback on failure - Names of c(a = 1) (`actual`) and c("a", "b") (`expected`) don't have the same values. - * Only in `expected`: "b" - + Code + expect_named(x1, c("a", "b"), ignore.order = TRUE) + Condition + Error: + ! Expected names(`x1`) to have the same values as `c("a", "b")`. + Actual: "a" + Expected: "a", "b" + Absent: "b" --- - Names of c(a = 1, b = 1) (`actual`) and c("a") (`expected`) don't have the same values. - * Only in `actual`: "b" - + Code + expect_named(x2, "a", ignore.order = TRUE) + Condition + Error: + ! Expected names(`x2`) to have the same values as "a". + Actual: "a", "b" + Expected: "a" + Needs: "b" --- - Names of c(a = 1) (`actual`) and c("b") (`expected`) don't have the same values. - * Only in `actual`: "a" - * Only in `expected`: "b" - + Code + expect_named(x1, "b", ignore.order = TRUE) + Condition + Error: + ! Expected names(`x1`) to have the same values as "b". + Actual: "a" + Expected: "b" + Needs: "a" + Absent: "b" --- - Names of c(a = 1) (`actual`) is not equal to c("a", "b") (`expected`). - - `actual`: "a" - `expected`: "a" "b" + Code + expect_named(x1, c("a", "b"), ignore.order = FALSE) + Condition + Error: + ! Expected names(`x1`) to be equal to `c("a", "b")`. + Differences: + `actual`: "a" + `expected`: "a" "b" --- - Names of c(a = 1, b = 1) (`actual`) is not equal to c("a") (`expected`). - - `actual`: "a" "b" - `expected`: "a" + Code + expect_named(x2, "a", ignore.order = FALSE) + Condition + Error: + ! Expected names(`x2`) to be equal to "a". + Differences: + `actual`: "a" "b" + `expected`: "a" --- - Names of c(a = 1) (`actual`) is not equal to c("b") (`expected`). - - `actual`: "a" - `expected`: "b" + Code + expect_named(x1, c("b"), ignore.order = FALSE) + Condition + Error: + ! Expected names(`x1`) to be equal to `c("b")`. + Differences: + `actual`: "a" + `expected`: "b" # expect_named validates its inputs diff --git a/tests/testthat/_snaps/expect-no-condition.md b/tests/testthat/_snaps/expect-no-condition.md index 1b1cdbeed..720491018 100644 --- a/tests/testthat/_snaps/expect-no-condition.md +++ b/tests/testthat/_snaps/expect-no-condition.md @@ -1,39 +1,73 @@ # expect_no_* conditions behave as expected - Expected `stop("error")` to run without any errors. - i Actually got a with text: - error + Code + expect_no_error(stop("error")) + Condition + Error: + ! Expected `stop("error")` not to throw any errors. + Actually got a with message: + error --- - Expected `warning("warning")` to run without any warnings. - i Actually got a with text: - warning + Code + expect_no_warning(warning("warning")) + Condition + Error: + ! Expected `warning("warning")` not to throw any warnings. + Actually got a with message: + warning --- - Expected `message("message")` to run without any messages. - i Actually got a with text: - message - + Code + expect_no_message(message("message")) + Condition + Error: + ! Expected `message("message")` not to throw any messages. + Actually got a with message: + message + --- - Expected `abort("error")` to run without any errors. - i Actually got a with text: - error + Code + expect_no_error(abort("error")) + Condition + Error: + ! Expected `abort("error")` not to throw any errors. + Actually got a with message: + error --- - Expected `warn("warning")` to run without any warnings. - i Actually got a with text: - warning + Code + expect_no_warning(warn("warning")) + Condition + Error: + ! Expected `warn("warning")` not to throw any warnings. + Actually got a with message: + warning --- - Expected `inform("message")` to run without any messages. - i Actually got a with text: - message + Code + expect_no_message(inform("message")) + Condition + Error: + ! Expected `inform("message")` not to throw any messages. + Actually got a with message: + message + +# expect_no_* don't emit success when they fail + + Code + expect_no_error(stop("!")) + Condition + Error: + ! Expected `stop("!")` not to throw any errors. + Actually got a with message: + ! # matched conditions give informative message @@ -41,28 +75,28 @@ expect_no_warning(foo()) Condition Error: - ! Expected `foo()` to run without any warnings. - i Actually got a with text: + ! Expected `foo()` not to throw any warnings. + Actually got a with message: This is a problem! Code expect_no_warning(foo(), message = "problem") Condition Error: - ! Expected `foo()` to run without any warnings matching pattern 'problem'. - i Actually got a with text: + ! Expected `foo()` not to throw any warnings matching pattern 'problem'. + Actually got a with message: This is a problem! Code expect_no_warning(foo(), class = "test") Condition Error: - ! Expected `foo()` to run without any warnings of class 'test'. - i Actually got a with text: + ! Expected `foo()` not to throw any warnings of class 'test'. + Actually got a with message: This is a problem! Code expect_no_warning(foo(), message = "problem", class = "test") Condition Error: - ! Expected `foo()` to run without any warnings of class 'test' matching pattern 'problem'. - i Actually got a with text: + ! Expected `foo()` not to throw any warnings of class 'test' matching pattern 'problem'. + Actually got a with message: This is a problem! diff --git a/tests/testthat/_snaps/expect-output.md b/tests/testthat/_snaps/expect-output.md index 29f86fa09..4a9d73b64 100644 --- a/tests/testthat/_snaps/expect-output.md +++ b/tests/testthat/_snaps/expect-output.md @@ -1,12 +1,38 @@ +# expect = NA checks for no output + + Code + expect_output(g(), NA) + Condition + Error: + ! Expected `g()` to produce no output. + Actual output: + ! + +# expect = NULL checks for some output + + Code + expect_output(f(), NULL) + Condition + Error: + ! Expected `f()` to produce output. + # expect = string checks for match - `g()` does not match regexp "x". - Output: - x | ! + Code + expect_output(g(), "x") + Condition + Error: + ! Expected `g()` to match regexp "x". + Actual output: + x | ! --- - "a" produced no output + Code + expect_output("a", "x") + Condition + Error: + ! Expected "a" to produce output. # expect_output validates its inputs diff --git a/tests/testthat/_snaps/expect-reference.md b/tests/testthat/_snaps/expect-reference.md new file mode 100644 index 000000000..65b91be88 --- /dev/null +++ b/tests/testthat/_snaps/expect-reference.md @@ -0,0 +1,8 @@ +# succeeds only when same object + + Code + expect_reference(x, 1) + Condition + Error: + ! Expected `x` to be a reference to 1. + diff --git a/tests/testthat/_snaps/expect-self-test.md b/tests/testthat/_snaps/expect-self-test.md index 8ac065b6d..ab0f27e58 100644 --- a/tests/testthat/_snaps/expect-self-test.md +++ b/tests/testthat/_snaps/expect-self-test.md @@ -1,8 +1,30 @@ -# expect_failure() can optionally match message +# expect_failure() generates a useful error messages - Failure message does not match regexp "banana". - Text: - x | apple + Code + expect_failure(expect_no_failure()) + Condition + Error: + ! Expected one failure. + Actually failed 0 times + Code + expect_failure(expect_many_failures()) + Condition + Error: + ! Expected one failure. + Actually failed 2 times + Code + expect_failure(expect_has_success()) + Condition + Error: + ! Expected zero successes. + Actually succeeded 1 times + Code + expect_failure(expect_failure_foo(), "bar") + Condition + Error: + ! Expected failure message to match regexp "bar". + Actual message: + x | foo # errors in expect_success bubble up @@ -18,10 +40,32 @@ show_failure(expect_true(FALSE)) Output Failed expectation: - FALSE (`actual`) is not equal to TRUE (`expected`). - + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + + +# expect_success() generates a useful error messages + + Code + expect_success(expect_no_success()) + Condition + Error: + ! Expected one success. + Actually succeeded 0 times + Code + expect_success(expect_many_successes()) + Condition + Error: + ! Expected one success. + Actually succeeded 2 times + Code + expect_success(expect_has_failure()) + Condition + Error: + ! Expected zero failures. + Actually failed 1 times # expect_no are deprecated diff --git a/tests/testthat/_snaps/expect-setequal.md b/tests/testthat/_snaps/expect-setequal.md index 14d806e2d..d4dad8744 100644 --- a/tests/testthat/_snaps/expect-setequal.md +++ b/tests/testthat/_snaps/expect-setequal.md @@ -21,68 +21,125 @@ # useful message on failure - "actual" (`actual`) and "expected" (`expected`) don't have the same values. - * Only in `actual`: "actual" - * Only in `expected`: "expected" - + Code + expect_setequal("actual", "expected") + Condition + Error: + ! Expected "actual" to have the same values as "expected". + Actual: "actual" + Expected: "expected" + Needs: "actual" + Absent: "expected" --- - 1:2 (`actual`) and 2 (`expected`) don't have the same values. - * Only in `actual`: 1 - + Code + expect_setequal(x, y) + Condition + Error: + ! Expected `x` to have the same values as `y`. + Actual: 1, 2 + Expected: 2 + Needs: 1 --- - 2 (`actual`) and 2:3 (`expected`) don't have the same values. - * Only in `expected`: 3 - + Code + expect_setequal(x, y) + Condition + Error: + ! Expected `x` to have the same values as `y`. + Actual: 2 + Expected: 2, 3 + Absent: 3 --- - 1:2 (`actual`) and 2:3 (`expected`) don't have the same values. - * Only in `actual`: 1 - * Only in `expected`: 3 - + Code + expect_setequal(x, y) + Condition + Error: + ! Expected `x` to have the same values as `y`. + Actual: 1, 2 + Expected: 2, 3 + Needs: 1 + Absent: 3 --- - c("a", "a") (`actual`) and c("b", "b", "b") (`expected`) don't have the same values. - * Only in `actual`: "a" - * Only in `expected`: "b" - + Code + expect_setequal(x, y) + Condition + Error: + ! Expected `x` to have the same values as `y`. + Actual: "a", "a" + Expected: "b", "b", "b" + Needs: "a" + Absent: "b" + +--- + + Code + expect_setequal(x, c("a", "b", "c", "d")) + Condition + Error: + ! Expected `x` to have the same values as `c("a", "b", "c", "d")`. + Actual: "a", "b", "c" + Expected: "a", "b", "c", "d" + Absent: "d" # truncates long vectors - 1:2 (`actual`) and 1:50 (`expected`) don't have the same values. - * Only in `expected`: 3, 4, 5, 6, 7, 8, 9, 10, 11, ... - + Code + expect_setequal(x, y) + Condition + Error: + ! Expected `x` to have the same values as `y`. + Actual: 1, 2 + Expected: 1, 2, 3, 4, 5, 6, 7, 8, 9, ... + Absent: 3, 4, 5, 6, 7, 8, 9, 10, 11, ... # expect_contains() gives useful message on failure - `x1` (`actual`) doesn't fully contain all the values in `x2` (`expected`). - * Missing from `actual`: "d" - * Present in `actual`: "a", "b", "c" - + Code + expect_contains(x1, x2) + Condition + Error: + ! Expected `x1` to contain all values in `x2`. + Actual: "a", "b", "c" + Expected: "c", "d" + Missing: "d" --- - `x1` (`actual`) doesn't fully contain all the values in `x3` (`expected`). - * Missing from `actual`: "d", "e" - * Present in `actual`: "a", "b", "c" - + Code + expect_contains(x1, x3) + Condition + Error: + ! Expected `x1` to contain all values in `x3`. + Actual: "a", "b", "c" + Expected: "d", "e" + Missing: "d", "e" # expect_in() gives useful message on failure - `x1` (`actual`) isn't fully contained within `x2` (`expected`). - * Missing from `expected`: "a" - * Present in `expected`: "b", "c" - + Code + expect_in(x1, x2) + Condition + Error: + ! Expected `x1` to only contain values from `x2`. + Actual: "a", "b" + Expected: "b", "c" + Invalid: "a" --- - `x1` (`actual`) isn't fully contained within `x3` (`expected`). - * Missing from `expected`: "a", "b" - * Present in `expected`: "d", "e" - + Code + expect_in(x1, x3) + Condition + Error: + ! Expected `x1` to only contain values from `x3`. + Actual: "a", "b" + Expected: "d", "e" + Invalid: "a", "b" diff --git a/tests/testthat/_snaps/expect-shape.md b/tests/testthat/_snaps/expect-shape.md index 95f5f7d66..f2b6c527b 100644 --- a/tests/testthat/_snaps/expect-shape.md +++ b/tests/testthat/_snaps/expect-shape.md @@ -1,3 +1,12 @@ +# generates actionable failure message + + Code + expect_length(x, 2) + Condition + Error: + ! Expected `x` to have length 2. + Actual length: 10. + # expect_length validates its inputs Code @@ -8,51 +17,108 @@ # dim compared correctly - matrix(nrow = 6, ncol = 3) has dim (6, 3), not (6, 2). + Code + expect_shape(matrix(nrow = 6, ncol = 3), dim = c(6L, 2L)) + Condition + Error: + ! Expected `matrix(nrow = 6, ncol = 3)` to have dim (6, 2). + Actual dim: (6, 3). --- - matrix(nrow = 6, ncol = 3) has dim (6, 3), not (7, 3). + Code + expect_shape(matrix(nrow = 6, ncol = 3), dim = c(7L, 3L)) + Condition + Error: + ! Expected `matrix(nrow = 6, ncol = 3)` to have dim (7, 3). + Actual dim: (6, 3). --- - array(dim = 1:3) has 3 dimensions, not 2. + Code + expect_shape(array(dim = 1:3), dim = 1:2) + Condition + Error: + ! Expected `array(dim = 1:3)` to have dim (1, 2). + Actual dim: (1, 2, 3). --- - array(dim = 1:3) has 3 dimensions, not 4. + Code + expect_shape(array(dim = 1:3), dim = 1:4) + Condition + Error: + ! Expected `array(dim = 1:3)` to have dim (1, 2, 3, 4). + Actual dim: (1, 2, 3). # nrow compared correctly - matrix(nrow = 5, ncol = 5) has 5 rows, not 6. + Code + expect_shape(matrix(nrow = 5, ncol = 5), nrow = 6L) + Condition + Error: + ! Expected `matrix(nrow = 5, ncol = 5)` to have 6 rows. + Actual rows: 5. --- - 1 has no dimensions. + Code + expect_shape(1, nrow = 1) + Condition + Error: + ! Expected 1 to have dimensions. # ncol compared correctly - matrix(nrow = 5, ncol = 5) has 5 columns, not 7. + Code + expect_shape(matrix(nrow = 5, ncol = 5), ncol = 7L) + Condition + Error: + ! Expected `matrix(nrow = 5, ncol = 5)` to have 7 columns. + Actual columns: 5. --- - array(1) has only one dimension. + Code + expect_shape(array(1), ncol = 1) + Condition + Error: + ! Expected `array(1)` to have two or more dimensions. --- - array(integer()) has only one dimension. + Code + expect_shape(array(integer()), ncol = 0L) + Condition + Error: + ! Expected `array(integer())` to have two or more dimensions. # NA handling (e.g. dbplyr) - `x` has NA rows, not 10. + Code + expect_shape(x, nrow = 10L) + Condition + Error: + ! Expected `x` to have 10 rows. + Actual rows: NA. --- - `x` has 10 columns, not NA. + Code + expect_shape(x, ncol = NA_integer_) + Condition + Error: + ! Expected `x` to have NA columns. + Actual columns: 10. --- - `x` has dim (NA, 10), not (10, NA). + Code + expect_shape(x, dim = c(10L, NA_integer_)) + Condition + Error: + ! Expected `x` to have dim (10, NA). + Actual dim: (NA, 10). # checks inputs arguments, diff --git a/tests/testthat/_snaps/expect-silent.md b/tests/testthat/_snaps/expect-silent.md new file mode 100644 index 000000000..cf5faa507 --- /dev/null +++ b/tests/testthat/_snaps/expect-silent.md @@ -0,0 +1,9 @@ +# generates useful failure message + + Code + expect_silent(f()) + Condition + Error: + ! Expected `f()` to run silently. + Actual noise: output, warnings, messages. + diff --git a/tests/testthat/_snaps/quasi-label.md b/tests/testthat/_snaps/quasi-label.md index 56cb164d1..853a3b923 100644 --- a/tests/testthat/_snaps/quasi-label.md +++ b/tests/testthat/_snaps/quasi-label.md @@ -4,23 +4,23 @@ expr_label(quote(foo(a = "this is a long argument", b = "this is a long argument", c = "this is a long argument"))) Output - [1] "foo(...)" + [1] "`foo(...)`" Code expr_label(quote(arg + arg + arg + arg + arg + arg + arg + arg + arg + arg + arg + arg)) Output - [1] "... + arg" + [1] "`... + arg`" Code expr_label(quote(arg + (arg + arg + arg + arg + arg + arg + arg + arg + arg + arg + arg))) Output - [1] "arg + ..." + [1] "`arg + ...`" Code expr_label(quote(function(a, b, c) { a + b + c })) Output - [1] "function(a, b, c) ..." + [1] "`function(a, b, c) ...`" # informative error for missing arg diff --git a/tests/testthat/_snaps/reporter-check.md b/tests/testthat/_snaps/reporter-check.md index a6bab3c70..7a9c8a9e9 100644 --- a/tests/testthat/_snaps/reporter-check.md +++ b/tests/testthat/_snaps/reporter-check.md @@ -3,34 +3,36 @@ [ FAIL 4 | WARN 1 | SKIP 3 | PASS 1 ] == Skipped tests (3) =========================================================== - * empty test (2): 'reporters/tests.R:40:1', 'reporters/tests.R:44:1' - * skip (1): 'reporters/tests.R:37:3' + * empty test (2): 'reporters/tests.R:41:1', 'reporters/tests.R:45:1' + * skip (1): 'reporters/tests.R:38:3' == Warnings ==================================================================== - -- Warning ('reporters/tests.R:46:5'): warnings get backtraces ----------------- + -- Warning ('reporters/tests.R:47:5'): warnings get backtraces ----------------- def Backtrace: x 1. \-f() == Failed tests ================================================================ - -- Failure ('reporters/tests.R:12:3'): Failure:1 ------------------------------- - FALSE (`actual`) is not equal to TRUE (`expected`). - + -- Failure ('reporters/tests.R:13:3'): Failure:1 ------------------------------- + Expected `x` to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - -- Failure ('reporters/tests.R:16:8'): Failure:2a ------------------------------ - FALSE (`actual`) is not equal to TRUE (`expected`). + -- Failure ('reporters/tests.R:17:8'): Failure:2a ------------------------------ + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() 2. \-testthat::expect_true(FALSE) - -- Error ('reporters/tests.R:23:3'): Error:1 ----------------------------------- + -- Error ('reporters/tests.R:24:3'): Error:1 ----------------------------------- Error in `eval(code, test_env)`: stop - -- Error ('reporters/tests.R:29:8'): errors get tracebacks --------------------- + -- Error ('reporters/tests.R:30:8'): errors get tracebacks --------------------- Error in `h()`: ! Backtrace: x @@ -59,34 +61,36 @@ [ FAIL 4 | WARN 1 | SKIP 3 | PASS 1 ] == Skipped tests (3) =========================================================== - * empty test (2): 'reporters/tests.R:40:1', 'reporters/tests.R:44:1' - * skip (1): 'reporters/tests.R:37:3' + * empty test (2): 'reporters/tests.R:41:1', 'reporters/tests.R:45:1' + * skip (1): 'reporters/tests.R:38:3' == Warnings ==================================================================== - -- Warning ('reporters/tests.R:46:5'): warnings get backtraces ----------------- + -- Warning ('reporters/tests.R:47:5'): warnings get backtraces ----------------- def Backtrace: x 1. \-f() == Failed tests ================================================================ - -- Failure ('reporters/tests.R:12:3'): Failure:1 ------------------------------- - FALSE (`actual`) is not equal to TRUE (`expected`). - + -- Failure ('reporters/tests.R:13:3'): Failure:1 ------------------------------- + Expected `x` to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - -- Failure ('reporters/tests.R:16:8'): Failure:2a ------------------------------ - FALSE (`actual`) is not equal to TRUE (`expected`). + -- Failure ('reporters/tests.R:17:8'): Failure:2a ------------------------------ + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() 2. \-testthat::expect_true(FALSE) - -- Error ('reporters/tests.R:23:3'): Error:1 ----------------------------------- + -- Error ('reporters/tests.R:24:3'): Error:1 ----------------------------------- Error in `eval(code, test_env)`: stop - -- Error ('reporters/tests.R:29:8'): errors get tracebacks --------------------- + -- Error ('reporters/tests.R:30:8'): errors get tracebacks --------------------- Error in `h()`: ! Backtrace: x diff --git a/tests/testthat/_snaps/reporter-debug.md b/tests/testthat/_snaps/reporter-debug.md index bcba41d51..4c639d157 100644 --- a/tests/testthat/_snaps/reporter-debug.md +++ b/tests/testthat/_snaps/reporter-debug.md @@ -1,12 +1,12 @@ # produces consistent output - 1: expect_true(FALSE) - 2: expect_waldo_equal_("equal", act, exp, info = info, ignore_attr = TRUE) + 1: expect_true(x) + 2: expect_waldo_constant_(act, exp, info = info, ignore_attr = TRUE) 3: fail(msg, info = info, trace_env = trace_env) 1: f() 2: expect_true(FALSE) - 3: expect_waldo_equal_("equal", act, exp, info = info, ignore_attr = TRUE) + 3: expect_waldo_constant_(act, exp, info = info, ignore_attr = TRUE) 4: fail(msg, info = info, trace_env = trace_env) 1: stop("stop") diff --git a/tests/testthat/_snaps/reporter-junit.md b/tests/testthat/_snaps/reporter-junit.md index 42c35bb3f..9250dbecf 100644 --- a/tests/testthat/_snaps/reporter-junit.md +++ b/tests/testthat/_snaps/reporter-junit.md @@ -7,16 +7,18 @@ - FALSE (`actual`) is not equal to TRUE (`expected`). - + Expected `x` to be TRUE. + Differences: `actual`: FALSE - `expected`: TRUE + `expected`: TRUE + - FALSE (`actual`) is not equal to TRUE (`expected`). - + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() @@ -25,10 +27,10 @@ - Error in `eval(code, test_env)`: stop + Error in `eval(code, test_env)`: stop - Error in `h()`: ! + Error in `h()`: ! Backtrace: x 1. \-f() @@ -38,10 +40,10 @@ - + - + @@ -52,7 +54,7 @@ 1. \-f() - + diff --git a/tests/testthat/_snaps/reporter-location.md b/tests/testthat/_snaps/reporter-location.md index 4da674543..9122561c1 100644 --- a/tests/testthat/_snaps/reporter-location.md +++ b/tests/testthat/_snaps/reporter-location.md @@ -5,32 +5,32 @@ End test: Success Start test: Failure:1 - 'reporters/tests.R:12:3' [failure] + 'reporters/tests.R:13:3' [failure] End test: Failure:1 Start test: Failure:2a - 'reporters/tests.R:16:8' [failure] + 'reporters/tests.R:17:8' [failure] End test: Failure:2a Start test: Error:1 - 'reporters/tests.R:23:3' [error] + 'reporters/tests.R:24:3' [error] End test: Error:1 Start test: errors get tracebacks - 'reporters/tests.R:29:8' [error] + 'reporters/tests.R:30:8' [error] End test: errors get tracebacks Start test: explicit skips are reported - 'reporters/tests.R:37:3' [skip] + 'reporters/tests.R:38:3' [skip] End test: explicit skips are reported Start test: empty tests are implicitly skipped - 'reporters/tests.R:40:1' [skip] + 'reporters/tests.R:41:1' [skip] End test: empty tests are implicitly skipped Start test: warnings get backtraces - 'reporters/tests.R:46:5' [warning] - 'reporters/tests.R:44:1' [skip] + 'reporters/tests.R:47:5' [warning] + 'reporters/tests.R:45:1' [skip] End test: warnings get backtraces diff --git a/tests/testthat/_snaps/reporter-progress.md b/tests/testthat/_snaps/reporter-progress.md index af5ed43d0..5599a4672 100644 --- a/tests/testthat/_snaps/reporter-progress.md +++ b/tests/testthat/_snaps/reporter-progress.md @@ -66,70 +66,81 @@ x | 11 0 | reporters/fail-many -------------------------------------------------------------------------------- Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). - + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + -------------------------------------------------------------------------------- Maximum number of failures exceeded; quitting at end of file. i Increase this number with (e.g.) `testthat::set_max_fails(Inf)` @@ -195,25 +206,22 @@ x 1. +-testthat::expect_s3_class(foo(), "foo") 2. | \-testthat::quasi_label(enquo(object)) - 3. | +-testthat:::labelled_value(...) - 4. | \-rlang::eval_bare(expr, quo_get_env(quo)) - 5. \-foo() + 3. | \-rlang::eval_bare(expr, quo_get_env(quo)) + 4. \-foo() Error ('reporters/backtraces.R:13:10'): errors thrown from a quasi-labelled argument are entraced (deep case) Error in `foo()`: foo Backtrace: - x - 1. +-testthat::expect_s3_class(f(), "foo") - 2. | \-testthat::quasi_label(enquo(object)) - 3. | +-testthat:::labelled_value(...) - 4. | \-rlang::eval_bare(expr, quo_get_env(quo)) - 5. \-f() - 6. \-g() - 7. +-testthat::expect_s3_class(foo(), "foo") - 8. | \-testthat::quasi_label(enquo(object)) - 9. | +-testthat:::labelled_value(...) - 10. | \-rlang::eval_bare(expr, quo_get_env(quo)) - 11. \-foo() + x + 1. +-testthat::expect_s3_class(f(), "foo") + 2. | \-testthat::quasi_label(enquo(object)) + 3. | \-rlang::eval_bare(expr, quo_get_env(quo)) + 4. \-f() + 5. \-g() + 6. +-testthat::expect_s3_class(foo(), "foo") + 7. | \-testthat::quasi_label(enquo(object)) + 8. | \-rlang::eval_bare(expr, quo_get_env(quo)) + 9. \-foo() Error ('reporters/backtraces.R:21:10'): errors thrown from a quasi-labelled argument are entraced (deep deep case) Error in `bar()`: foobar @@ -223,10 +231,9 @@ 2. \-g() 3. +-testthat::expect_s3_class(foo(), "foo") 4. | \-testthat::quasi_label(enquo(object)) - 5. | +-testthat:::labelled_value(...) - 6. | \-rlang::eval_bare(expr, quo_get_env(quo)) - 7. \-foo() - 8. \-bar() + 5. | \-rlang::eval_bare(expr, quo_get_env(quo)) + 6. \-foo() + 7. \-bar() Error ('reporters/backtraces.R:32:16'): failed expect_error() prints a backtrace Error in `signaller()`: bar @@ -287,10 +294,11 @@ 26. \-f(x - 1) Failure ('reporters/backtraces.R:62:6'): (code run outside of `test_that()`) - FALSE (`actual`) is not equal to TRUE (`expected`). - + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() @@ -299,10 +307,11 @@ 4. \-testthat::expect_true(FALSE) Failure ('reporters/backtraces.R:67:3'): nested expectations get backtraces - FALSE (`actual`) is not equal to TRUE (`expected`). - + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() @@ -326,25 +335,22 @@ x 1. +-testthat::expect_s3_class(foo(), "foo") 2. | \-testthat::quasi_label(enquo(object)) - 3. | +-testthat:::labelled_value(...) - 4. | \-rlang::eval_bare(expr, quo_get_env(quo)) - 5. \-foo() + 3. | \-rlang::eval_bare(expr, quo_get_env(quo)) + 4. \-foo() Error ('reporters/backtraces.R:13:10'): errors thrown from a quasi-labelled argument are entraced (deep case) Error in `foo()`: foo Backtrace: - x - 1. +-testthat::expect_s3_class(f(), "foo") - 2. | \-testthat::quasi_label(enquo(object)) - 3. | +-testthat:::labelled_value(...) - 4. | \-rlang::eval_bare(expr, quo_get_env(quo)) - 5. \-f() - 6. \-g() - 7. +-testthat::expect_s3_class(foo(), "foo") - 8. | \-testthat::quasi_label(enquo(object)) - 9. | +-testthat:::labelled_value(...) - 10. | \-rlang::eval_bare(expr, quo_get_env(quo)) - 11. \-foo() + x + 1. +-testthat::expect_s3_class(f(), "foo") + 2. | \-testthat::quasi_label(enquo(object)) + 3. | \-rlang::eval_bare(expr, quo_get_env(quo)) + 4. \-f() + 5. \-g() + 6. +-testthat::expect_s3_class(foo(), "foo") + 7. | \-testthat::quasi_label(enquo(object)) + 8. | \-rlang::eval_bare(expr, quo_get_env(quo)) + 9. \-foo() Error ('reporters/backtraces.R:21:10'): errors thrown from a quasi-labelled argument are entraced (deep deep case) Error in `bar()`: foobar @@ -354,10 +360,9 @@ 2. \-g() 3. +-testthat::expect_s3_class(foo(), "foo") 4. | \-testthat::quasi_label(enquo(object)) - 5. | +-testthat:::labelled_value(...) - 6. | \-rlang::eval_bare(expr, quo_get_env(quo)) - 7. \-foo() - 8. \-bar() + 5. | \-rlang::eval_bare(expr, quo_get_env(quo)) + 6. \-foo() + 7. \-bar() Error ('reporters/backtraces.R:32:16'): failed expect_error() prints a backtrace Error in `signaller()`: bar @@ -411,10 +416,11 @@ 26. \-f(x - 1) Failure ('reporters/backtraces.R:62:6'): (code run outside of `test_that()`) - FALSE (`actual`) is not equal to TRUE (`expected`). - + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() @@ -423,10 +429,11 @@ 4. \-testthat::expect_true(FALSE) Failure ('reporters/backtraces.R:67:3'): nested expectations get backtraces - FALSE (`actual`) is not equal to TRUE (`expected`). - + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() @@ -468,17 +475,19 @@ [ FAIL 1 | WARN 0 | SKIP 0 | PASS 1 ] [ FAIL 2 | WARN 0 | SKIP 0 | PASS 1 ] - -- Failure ('reporters/tests.R:12:3'): Failure:1 ------------------------------- - FALSE (`actual`) is not equal to TRUE (`expected`). - + -- Failure ('reporters/tests.R:13:3'): Failure:1 ------------------------------- + Expected `x` to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - -- Failure ('reporters/tests.R:16:8'): Failure:2a ------------------------------ - FALSE (`actual`) is not equal to TRUE (`expected`). + -- Failure ('reporters/tests.R:17:8'): Failure:2a ------------------------------ + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() @@ -488,10 +497,10 @@ [ FAIL 3 | WARN 0 | SKIP 0 | PASS 1 ] [ FAIL 4 | WARN 0 | SKIP 0 | PASS 1 ] - -- Error ('reporters/tests.R:23:3'): Error:1 ----------------------------------- + -- Error ('reporters/tests.R:24:3'): Error:1 ----------------------------------- Error in `eval(code, test_env)`: stop - -- Error ('reporters/tests.R:29:8'): errors get tracebacks --------------------- + -- Error ('reporters/tests.R:30:8'): errors get tracebacks --------------------- Error in `h()`: ! Backtrace: x @@ -506,15 +515,15 @@ [ FAIL 4 | WARN 1 | SKIP 2 | PASS 1 ] [ FAIL 4 | WARN 1 | SKIP 3 | PASS 1 ] - -- Warning ('reporters/tests.R:46:5'): warnings get backtraces ----------------- + -- Warning ('reporters/tests.R:47:5'): warnings get backtraces ----------------- def Backtrace: x 1. \-f() -- Skipped tests (3) ----------------------------------------------------------- - * empty test (2): 'reporters/tests.R:40:1', 'reporters/tests.R:44:1' - * skip (1): 'reporters/tests.R:37:3' + * empty test (2): 'reporters/tests.R:41:1', 'reporters/tests.R:45:1' + * skip (1): 'reporters/tests.R:38:3' [ FAIL 4 | WARN 1 | SKIP 3 | PASS 1 ] @@ -567,70 +576,81 @@ x | 11 0 | reporters/fail-many -------------------------------------------------------------------------------- Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). - + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - Failure ('reporters/fail-many.R:3:5'): Example - FALSE (`actual`) is not equal to TRUE (`expected`). + Failure ('reporters/fail-many.R:3:5'): Example + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + -------------------------------------------------------------------------------- Maximum number of failures exceeded; quitting at end of file. i Increase this number with (e.g.) `testthat::set_max_fails(Inf)` diff --git a/tests/testthat/_snaps/reporter-rstudio.md b/tests/testthat/_snaps/reporter-rstudio.md index 4c96a13b9..67ec5a893 100644 --- a/tests/testthat/_snaps/reporter-rstudio.md +++ b/tests/testthat/_snaps/reporter-rstudio.md @@ -1,11 +1,11 @@ # reporter basics works - 'reporters/tests.R:12:3' [failure] Failure:1. FALSE (`actual`) is not equal to TRUE (`expected`). - 'reporters/tests.R:16:8' [failure] Failure:2a. FALSE (`actual`) is not equal to TRUE (`expected`). - 'reporters/tests.R:23:3' [error] Error:1. Error in `eval(code, test_env)`: stop - 'reporters/tests.R:29:8' [error] errors get tracebacks. Error in `h()`: ! - 'reporters/tests.R:37:3' [skip] explicit skips are reported. Reason: skip - 'reporters/tests.R:40:1' [skip] empty tests are implicitly skipped. Reason: empty test - 'reporters/tests.R:46:5' [warning] warnings get backtraces. def - 'reporters/tests.R:44:1' [skip] warnings get backtraces. Reason: empty test + 'reporters/tests.R:13:3' [failure] Failure:1. Expected `x` to be TRUE. + 'reporters/tests.R:17:8' [failure] Failure:2a. Expected FALSE to be TRUE. + 'reporters/tests.R:24:3' [error] Error:1. Error in `eval(code, test_env)`: stop + 'reporters/tests.R:30:8' [error] errors get tracebacks. Error in `h()`: ! + 'reporters/tests.R:38:3' [skip] explicit skips are reported. Reason: skip + 'reporters/tests.R:41:1' [skip] empty tests are implicitly skipped. Reason: empty test + 'reporters/tests.R:47:5' [warning] warnings get backtraces. def + 'reporters/tests.R:45:1' [skip] warnings get backtraces. Reason: empty test diff --git a/tests/testthat/_snaps/reporter-stop.md b/tests/testthat/_snaps/reporter-stop.md index 79a6d4681..c57289735 100644 --- a/tests/testthat/_snaps/reporter-stop.md +++ b/tests/testthat/_snaps/reporter-stop.md @@ -1,25 +1,27 @@ # produces useful output - -- Failure ('reporters/tests.R:12:3'): Failure:1 ------------------------------- - FALSE (`actual`) is not equal to TRUE (`expected`). - + -- Failure ('reporters/tests.R:13:3'): Failure:1 ------------------------------- + Expected `x` to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - -- Failure ('reporters/tests.R:16:8'): Failure:2a ------------------------------ - FALSE (`actual`) is not equal to TRUE (`expected`). + -- Failure ('reporters/tests.R:17:8'): Failure:2a ------------------------------ + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() 2. \-testthat::expect_true(FALSE) - -- Error ('reporters/tests.R:23:3'): Error:1 ----------------------------------- + -- Error ('reporters/tests.R:24:3'): Error:1 ----------------------------------- Error in `eval(code, test_env)`: stop - -- Error ('reporters/tests.R:29:8'): errors get tracebacks --------------------- + -- Error ('reporters/tests.R:30:8'): errors get tracebacks --------------------- Error in `h()`: ! Backtrace: x @@ -27,19 +29,19 @@ 2. \-g() 3. \-h() - -- Skip ('reporters/tests.R:37:3'): explicit skips are reported ---------------- + -- Skip ('reporters/tests.R:38:3'): explicit skips are reported ---------------- Reason: skip - -- Skip ('reporters/tests.R:40:1'): empty tests are implicitly skipped --------- + -- Skip ('reporters/tests.R:41:1'): empty tests are implicitly skipped --------- Reason: empty test - -- Warning ('reporters/tests.R:46:5'): warnings get backtraces ----------------- + -- Warning ('reporters/tests.R:47:5'): warnings get backtraces ----------------- def Backtrace: x 1. \-f() - -- Skip ('reporters/tests.R:44:1'): warnings get backtraces -------------------- + -- Skip ('reporters/tests.R:45:1'): warnings get backtraces -------------------- Reason: empty test diff --git a/tests/testthat/_snaps/reporter-summary.md b/tests/testthat/_snaps/reporter-summary.md index 30b0bc59b..84f65a891 100644 --- a/tests/testthat/_snaps/reporter-summary.md +++ b/tests/testthat/_snaps/reporter-summary.md @@ -8,36 +8,38 @@ Warnings: WS == Skipped ===================================================================== - 1. explicit skips are reported ('reporters/tests.R:37:3') - Reason: skip + 1. explicit skips are reported ('reporters/tests.R:38:3') - Reason: skip - 2. empty tests are implicitly skipped ('reporters/tests.R:40:1') - Reason: empty test + 2. empty tests are implicitly skipped ('reporters/tests.R:41:1') - Reason: empty test - 3. warnings get backtraces ('reporters/tests.R:44:1') - Reason: empty test + 3. warnings get backtraces ('reporters/tests.R:45:1') - Reason: empty test == Warnings ==================================================================== - 1. warnings get backtraces ('reporters/tests.R:46:5') - def + 1. warnings get backtraces ('reporters/tests.R:47:5') - def == Failed ====================================================================== - -- 1. Failure ('reporters/tests.R:12:3'): Failure:1 ---------------------------- - FALSE (`actual`) is not equal to TRUE (`expected`). - + -- 1. Failure ('reporters/tests.R:13:3'): Failure:1 ---------------------------- + Expected `x` to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - -- 2. Failure ('reporters/tests.R:16:8'): Failure:2a --------------------------- - FALSE (`actual`) is not equal to TRUE (`expected`). + -- 2. Failure ('reporters/tests.R:17:8'): Failure:2a --------------------------- + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() 2. \-testthat::expect_true(FALSE) - -- 3. Error ('reporters/tests.R:23:3'): Error:1 -------------------------------- + -- 3. Error ('reporters/tests.R:24:3'): Error:1 -------------------------------- Error in `eval(code, test_env)`: stop - -- 4. Error ('reporters/tests.R:29:8'): errors get tracebacks ------------------ + -- 4. Error ('reporters/tests.R:30:8'): errors get tracebacks ------------------ Error in `h()`: ! Backtrace: x @@ -57,36 +59,38 @@ Warnings: WS == Skipped ===================================================================== - 1. explicit skips are reported ('reporters/tests.R:37:3') - Reason: skip + 1. explicit skips are reported ('reporters/tests.R:38:3') - Reason: skip - 2. empty tests are implicitly skipped ('reporters/tests.R:40:1') - Reason: empty test + 2. empty tests are implicitly skipped ('reporters/tests.R:41:1') - Reason: empty test - 3. warnings get backtraces ('reporters/tests.R:44:1') - Reason: empty test + 3. warnings get backtraces ('reporters/tests.R:45:1') - Reason: empty test == Warnings ==================================================================== - 1. warnings get backtraces ('reporters/tests.R:46:5') - def + 1. warnings get backtraces ('reporters/tests.R:47:5') - def == Failed ====================================================================== - -- 1. Failure ('reporters/tests.R:12:3'): Failure:1 ---------------------------- - FALSE (`actual`) is not equal to TRUE (`expected`). - + -- 1. Failure ('reporters/tests.R:13:3'): Failure:1 ---------------------------- + Expected `x` to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - -- 2. Failure ('reporters/tests.R:16:8'): Failure:2a --------------------------- - FALSE (`actual`) is not equal to TRUE (`expected`). + -- 2. Failure ('reporters/tests.R:17:8'): Failure:2a --------------------------- + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() 2. \-testthat::expect_true(FALSE) - -- 3. Error ('reporters/tests.R:23:3'): Error:1 -------------------------------- + -- 3. Error ('reporters/tests.R:24:3'): Error:1 -------------------------------- Error in `eval(code, test_env)`: stop - -- 4. Error ('reporters/tests.R:29:8'): errors get tracebacks ------------------ + -- 4. Error ('reporters/tests.R:30:8'): errors get tracebacks ------------------ Error in `h()`: ! Backtrace: x @@ -106,27 +110,29 @@ Warnings: WS == Skipped ===================================================================== - 1. explicit skips are reported ('reporters/tests.R:37:3') - Reason: skip + 1. explicit skips are reported ('reporters/tests.R:38:3') - Reason: skip - 2. empty tests are implicitly skipped ('reporters/tests.R:40:1') - Reason: empty test + 2. empty tests are implicitly skipped ('reporters/tests.R:41:1') - Reason: empty test - 3. warnings get backtraces ('reporters/tests.R:44:1') - Reason: empty test + 3. warnings get backtraces ('reporters/tests.R:45:1') - Reason: empty test == Warnings ==================================================================== - 1. warnings get backtraces ('reporters/tests.R:46:5') - def + 1. warnings get backtraces ('reporters/tests.R:47:5') - def == Failed ====================================================================== - -- 1. Failure ('reporters/tests.R:12:3'): Failure:1 ---------------------------- - FALSE (`actual`) is not equal to TRUE (`expected`). - + -- 1. Failure ('reporters/tests.R:13:3'): Failure:1 ---------------------------- + Expected `x` to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - -- 2. Failure ('reporters/tests.R:16:8'): Failure:2a --------------------------- - FALSE (`actual`) is not equal to TRUE (`expected`). + -- 2. Failure ('reporters/tests.R:17:8'): Failure:2a --------------------------- + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() diff --git a/tests/testthat/_snaps/reporter-tap.md b/tests/testthat/_snaps/reporter-tap.md index 3ae84629f..0911c0dd0 100644 --- a/tests/testthat/_snaps/reporter-tap.md +++ b/tests/testthat/_snaps/reporter-tap.md @@ -5,15 +5,17 @@ ok 1 Success # Context Failures not ok 2 Failure:1 - FALSE (`actual`) is not equal to TRUE (`expected`). - + Expected `x` to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE - not ok 3 Failure:2a - FALSE (`actual`) is not equal to TRUE (`expected`). + not ok 3 Failure:2a + Expected FALSE to be TRUE. + Differences: `actual`: FALSE `expected`: TRUE + Backtrace: x 1. \-f() diff --git a/tests/testthat/_snaps/reporter-teamcity.md b/tests/testthat/_snaps/reporter-teamcity.md index 0fbc5ce4c..2fed7d44e 100644 --- a/tests/testthat/_snaps/reporter-teamcity.md +++ b/tests/testthat/_snaps/reporter-teamcity.md @@ -12,13 +12,13 @@ ##teamcity[testSuiteStarted name='Failures'] ##teamcity[testSuiteStarted name='Failure:1'] ##teamcity[testStarted name='expectation 1'] - ##teamcity[testFailed name='expectation 1' message='FALSE (`actual`) is not equal to TRUE (`expected`).' details='|n`actual`: FALSE|n`expected`: TRUE '] + ##teamcity[testFailed name='expectation 1' message='Expected `x` to be TRUE.' details='Differences:|n`actual`: FALSE|n`expected`: TRUE '] ##teamcity[testFinished name='expectation 1'] ##teamcity[testSuiteFinished name='Failure:1'] ##teamcity[testSuiteStarted name='Failure:2a'] ##teamcity[testStarted name='expectation 1'] - ##teamcity[testFailed name='expectation 1' message='FALSE (`actual`) is not equal to TRUE (`expected`).' details='|n`actual`: FALSE|n`expected`: TRUE |nBacktrace:|n x|n 1. \-f()|n 2. \-testthat::expect_true(FALSE)'] + ##teamcity[testFailed name='expectation 1' message='Expected FALSE to be TRUE.' details='Differences:|n`actual`: FALSE|n`expected`: TRUE |n|nBacktrace:|n x|n 1. \-f()|n 2. \-testthat::expect_true(FALSE)'] ##teamcity[testFinished name='expectation 1'] ##teamcity[testSuiteFinished name='Failure:2a'] diff --git a/tests/testthat/_snaps/snapshot-reporter.md b/tests/testthat/_snaps/snapshot-reporter.md new file mode 100644 index 000000000..4d0b6fe51 --- /dev/null +++ b/tests/testthat/_snaps/snapshot-reporter.md @@ -0,0 +1,7 @@ +# `expect_error()` can fail inside `expect_snapshot()` + + Code + err$message + Output + [1] "Expected NULL to throw a error." + diff --git a/tests/testthat/_snaps/snapshot.md b/tests/testthat/_snaps/snapshot.md index 04c746a42..f9faacec4 100644 --- a/tests/testthat/_snaps/snapshot.md +++ b/tests/testthat/_snaps/snapshot.md @@ -96,6 +96,14 @@ Output [1] "****" +# always checks error status + + Code + expect_snapshot(print("!"), error = TRUE) + Condition + Error: + ! Expected `print("!")` to throw a error. + # can capture error/warning messages This is an error diff --git a/tests/testthat/_snaps/try-again.md b/tests/testthat/_snaps/try-again.md index 68475e7a9..623946499 100644 --- a/tests/testthat/_snaps/try-again.md +++ b/tests/testthat/_snaps/try-again.md @@ -14,8 +14,8 @@ i Expectation failed; trying again (1)... Condition Error: - ! `i` (`actual`) is not equal to 0 (`expected`). - + ! Expected `i` to be equal to 0. + Differences: `actual`: 1.0 `expected`: 0.0 diff --git a/tests/testthat/reporters/tests.R b/tests/testthat/reporters/tests.R index 3e22e6c0d..8029f681b 100644 --- a/tests/testthat/reporters/tests.R +++ b/tests/testthat/reporters/tests.R @@ -9,7 +9,8 @@ test_that("Success", { context("Failures") test_that("Failure:1", { - expect_true(FALSE) + x <- FALSE + expect_true(x) }) test_that("Failure:2a", { diff --git a/tests/testthat/test-compare.R b/tests/testthat/test-compare.R index 87dd9f30a..742765a29 100644 --- a/tests/testthat/test-compare.R +++ b/tests/testthat/test-compare.R @@ -40,7 +40,7 @@ test_that("classes must be identical", { c1 <- "a" c2 <- structure("a", class = "mycharacter") - expect_match(compare(c1, c2)$message, "'character' is not 'mycharacter'") + expect_match(compare(c1, c2)$message, "\"character\" is not \"mycharacter\"") }) test_that("attributes must be identical", { @@ -119,8 +119,11 @@ test_that("classes must be identical", { f1 <- factor("a") f2 <- factor("a", ordered = TRUE) - expect_match(compare(1L, f1)$message, "'integer' is not 'factor'") - expect_match(compare(1L, f2)$message, "'integer' is not 'ordered'/'factor'") + expect_match(compare(1L, f1)$message, "\"integer\" is not \"factor\"") + expect_match( + compare(1L, f2)$message, + "\"integer\" is not \"ordered\"/\"factor\"" + ) }) test_that("attributes must be identical", { @@ -184,7 +187,7 @@ test_that("both POSIXt classes are compatible", { test_that("other classes are not", { expect_match( compare(Sys.time(), 1)$message, - "'POSIXct'/'POSIXt' is not 'numeric'" + "\"POSIXct\"/\"POSIXt\" is not \"numeric\"" ) }) diff --git a/tests/testthat/test-expect-comparison.R b/tests/testthat/test-expect-comparison.R index b1ea3e279..bfb7e0fa1 100644 --- a/tests/testthat/test-expect-comparison.R +++ b/tests/testthat/test-expect-comparison.R @@ -1,11 +1,12 @@ test_that("basic comparisons work", { - expect_success(expect_lt(10, 11)) - expect_failure(expect_lt(10, 10)) - expect_success(expect_lte(10, 10)) + x <- 10 + expect_success(expect_lt(x, 11)) + expect_snapshot_failure(expect_lt(x, 10)) + expect_success(expect_lte(x, 10)) expect_success(expect_gt(11, 10)) - expect_failure(expect_gt(10, 10)) - expect_success(expect_gte(10, 10)) + expect_snapshot_failure(expect_gt(x, 10)) + expect_success(expect_gte(x, 10)) }) test_that("useful output when numbers are very small", { @@ -37,6 +38,9 @@ test_that("comparisons with Inf work", { expect_success(expect_gt(Inf, 10)) expect_failure(expect_gt(Inf, Inf)) expect_success(expect_gte(Inf, Inf)) + + x <- Inf + expect_snapshot_failure(expect_lt(x, Inf)) }) test_that("comparisons with NA work", { @@ -49,6 +53,9 @@ test_that("comparisons with NA work", { expect_failure(expect_gt(NA_real_, 10)) expect_failure(expect_gt(NA_real_, NA_real_)) expect_failure(expect_gte(NA_real_, NA_real_)) + + x <- NA_real_ + expect_snapshot_failure(expect_lt(x, 10)) }) test_that("comparisons with more complicated objects work", { diff --git a/tests/testthat/test-expect-condition.R b/tests/testthat/test-expect-condition.R index aefa2b8b3..78a6efdf9 100644 --- a/tests/testthat/test-expect-condition.R +++ b/tests/testthat/test-expect-condition.R @@ -5,18 +5,24 @@ test_that("returns condition or value", { test_that("regexp = NULL checks for presence of error", { expect_success(expect_error(stop())) - expect_snapshot_failure(expect_error({})) + + f <- function() {} + expect_snapshot_failure(expect_error(f())) }) test_that("regexp = NA checks for absence of error", { expect_success(expect_error({}, NA)) - expect_failure(expect_error(stop("Yes"), NA)) + + f <- function() stop("Yes") + expect_snapshot_failure(expect_error(f(), NA)) }) test_that("regexp = string matches for error message", { expect_success(expect_error(stop("Yes"), "Yes")) expect_error(expect_error(stop("Yes"), "No")) - expect_snapshot_failure(expect_error("OK", "No")) + + f <- function() {} + expect_snapshot_failure(expect_error(f(), "No")) }) test_that("class = string matches class of error", { @@ -25,6 +31,7 @@ test_that("class = string matches class of error", { } expect_success(expect_error(blah(), class = "blah")) + # otherwise bubbles up expect_error(expect_error(blah(), class = "blech"), class = "blah") }) @@ -147,7 +154,9 @@ test_that("expect_warning validates its inputs", { test_that("regexp = NA checks for absence of message", { expect_success(expect_message({}, NA)) - expect_failure(expect_message(message("!"), NA)) + + f <- \() message("!") + expect_snapshot_failure(expect_message(f(), NA)) }) test_that("expect_message validates its inputs", { diff --git a/tests/testthat/test-expect-constant.R b/tests/testthat/test-expect-constant.R index b26ea3c9d..f249277fc 100644 --- a/tests/testthat/test-expect-constant.R +++ b/tests/testthat/test-expect-constant.R @@ -1,11 +1,11 @@ test_that("logical tests act as expected", { - local_output_override() + df <- data.frame(1:10) expect_success(expect_true(TRUE)) - expect_success(expect_false(FALSE)) + expect_snapshot_failure(expect_true(df)) - expect_snapshot_failure(expect_true(FALSE)) - expect_snapshot_failure(expect_false(TRUE)) + expect_success(expect_false(FALSE)) + expect_snapshot_failure(expect_false(df)) }) test_that("logical tests ignore attributes", { @@ -13,22 +13,17 @@ test_that("logical tests ignore attributes", { expect_success(expect_false(c(a = FALSE))) }) -test_that("can compare non-vectors", { - local_output_override() - expect_snapshot_failure(expect_true(quote(x))) -}) - test_that("additional info returned in message", { - expect_failure(expect_true(FALSE, "NOPE"), "\nNOPE") - expect_failure(expect_false(TRUE, "YUP"), "\nYUP") + expect_failure(expect_true(FALSE, "NOPE"), "NOPE") + expect_failure(expect_false(TRUE, "YUP"), "YUP") }) test_that("expect_null works", { - local_output_override() + x <- NULL + df <- data.frame(1:10) - expect_success(expect_null(NULL)) - expect_snapshot_failure(expect_null(1L)) - expect_snapshot_failure(expect_null(environment())) + expect_success(expect_null(x)) + expect_snapshot_failure(expect_null(df)) }) test_that("returns the input value", { diff --git a/tests/testthat/test-expect-equality.R b/tests/testthat/test-expect-equality.R index 1d3b5d702..5924870d2 100644 --- a/tests/testthat/test-expect-equality.R +++ b/tests/testthat/test-expect-equality.R @@ -61,66 +61,49 @@ test_that("second edition only optionally sets tolerance", { expect_success(expect_equal(x, y)) }) -test_that("provide useful feedback on failure", { - local_output_override() - - local_edition(3) - expect_snapshot_error(expect_identical(1, "a")) - expect_snapshot_error(expect_equal(1, "a")) +test_that("provide useful feedback on failure (3e)", { + x <- 1 + expect_snapshot_failure(expect_identical(x, "a")) + expect_snapshot_failure(expect_equal(x, "a")) local_edition(2) withr::local_options(testthat.edition_ignore = TRUE) - expect_snapshot_error(expect_identical(1, "a")) - expect_snapshot_error(expect_equal(1, "a")) + expect_snapshot_failure(expect_identical(x, "a")) + expect_snapshot_failure(expect_equal(x, "a")) }) -test_that("default labels use unquoting", { +test_that("provide useful feedback on failure (2e)", { local_edition(2) + withr::local_options(testthat.edition_ignore = TRUE) + + x <- 1 + expect_snapshot_failure(expect_identical(x, "a")) + expect_snapshot_failure(expect_equal(x, "a")) +}) - x <- 2 - expect_failure(expect_equal(1, !!x), "1 not equal to 2", fixed = TRUE) +test_that("default labels use unquoting", { + x <- 1 + y <- 2 + expect_snapshot_failure(expect_equal(x, !!y)) }) test_that("% is not treated as sprintf format specifier (#445)", { expect_failure(expect_equal("+", "%")) expect_failure(expect_equal("%", "+")) - expect_equal("%", "%") + expect_success(expect_equal("%", "%")) }) -test_that("is_call_infix() handles complex calls (#1472)", { - expect_false(is_call_infix(quote( - base::any( - c( - veryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy_long_name = TRUE - ), - na.rm = TRUE - ) - ))) - - withr::local_envvar( - "_R_CHECK_LENGTH_1_LOGIC2_" = "TRUE", - ) - expect_true( - base::any( - c( - veryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy_long_name = TRUE - ), - na.rm = TRUE - ) - ) -}) - - # 2nd edition --------------------------------------------------- test_that("useful message if objects equal but not identical", { local_edition(2) f <- function() x + environment(f) <- new_environment() g <- function() x - environment(g) <- globalenv() + environment(g) <- new_environment() - expect_failure(expect_identical(f, g)) + expect_snapshot_failure(expect_identical(f, g)) }) test_that("attributes for object (#452)", { @@ -128,7 +111,7 @@ test_that("attributes for object (#452)", { oops <- structure(0, oops = "oops") expect_equal(oops, oops) - expect_failure(expect_equal(oops, 0)) + expect_snapshot_failure(expect_equal(oops, 0)) expect_equal(as.numeric(oops), 0) }) diff --git a/tests/testthat/test-expect-inheritance.R b/tests/testthat/test-expect-inheritance.R index 24f5af239..c75be5bd0 100644 --- a/tests/testthat/test-expect-inheritance.R +++ b/tests/testthat/test-expect-inheritance.R @@ -1,6 +1,8 @@ test_that("expect_type checks typeof", { expect_success(expect_type(factor("a"), "integer")) - expect_failure(expect_type(factor("a"), "double")) + + x <- factor("a") + expect_snapshot_failure(expect_type(x, "double")) }) test_that("expect_type validates its inputs", { @@ -13,15 +15,19 @@ test_that("expect_is checks class", { local_edition(2) expect_success(expect_is(factor("a"), "factor")) - expect_failure(expect_is(factor("a"), "integer")) + expect_snapshot_failure(expect_is(factor("a"), "integer")) }) test_that("expect_s3/s4_class fails if appropriate type", { A <- methods::setClass("A", contains = "list") - expect_failure(expect_s3_class(1, "double"), "not an S3 object") - expect_failure(expect_s3_class(A(), "double"), "not an S3 object") - expect_failure(expect_s4_class(factor(), "double"), "not an S4 object") + x1 <- 1 + x2 <- A() + x3 <- factor("a") + + expect_snapshot_failure(expect_s3_class(x1, "double")) + expect_snapshot_failure(expect_s3_class(x2, "double")) + expect_snapshot_failure(expect_s4_class(x3, "double")) }) test_that("expect_s[34]_class can check not S3/S4", { @@ -65,7 +71,7 @@ test_that("test_s3_class respects class hierarchy", { test_that("test_s3_class can request exact match", { x <- structure(list(), class = c("a", "b")) - expect_failure(expect_s3_class(x, "a", exact = TRUE)) + expect_snapshot_failure(expect_s3_class(x, "a", exact = TRUE)) expect_success(expect_s3_class(x, c("a", "b"), exact = TRUE)) }) @@ -121,7 +127,6 @@ test_that("expect_r6_class validates its inputs", { # expect_s7_class -------------------------------------------------------- test_that("can check with actual class", { - skip_if_not_installed("S7") Foo <- S7::new_class("Foo", package = NULL) Bar <- S7::new_class("Bar", package = NULL) expect_success(expect_s7_class(Foo(), class = Foo)) @@ -131,6 +136,13 @@ test_that("can check with actual class", { expect_snapshot_failure(expect_s7_class(Baz(), class = Bar)) }) +test_that("informative failure if not S7", { + Foo <- S7::new_class("Foo", package = NULL) + + x <- factor() + expect_snapshot_failure(expect_s7_class(x, Foo)) +}) + test_that("expect_s7_class validates its inputs", { skip_if_not_installed("S7") expect_snapshot(expect_s7_class(1, 1), error = TRUE) diff --git a/tests/testthat/test-expect-invisible.R b/tests/testthat/test-expect-invisible.R index b2b0a2c81..08075c5e0 100644 --- a/tests/testthat/test-expect-invisible.R +++ b/tests/testthat/test-expect-invisible.R @@ -1,9 +1,9 @@ test_that("basic principles of visibility hold", { expect_success(expect_invisible(x <- 10)) - expect_failure(expect_invisible(x)) + expect_snapshot_failure(expect_invisible(x)) expect_success(expect_visible(x)) - expect_failure(expect_visible(x <- 1)) + expect_snapshot_failure(expect_visible(x <- 1)) }) test_that("generates useful failure messages", { diff --git a/tests/testthat/test-expect-match.R b/tests/testthat/test-expect-match.R index abe57887b..342d84669 100644 --- a/tests/testthat/test-expect-match.R +++ b/tests/testthat/test-expect-match.R @@ -1,11 +1,18 @@ -test_that("generates useful failure messages", { - local_reproducible_output(unicode = TRUE) - +test_that("useful failure if empty", { zero <- character(0) expect_snapshot_failure(expect_match(zero, 'asdf')) +}) + +test_that("useful failure messages for scalars", { + local_reproducible_output(unicode = TRUE) one <- "bcde" expect_snapshot_failure(expect_match(one, 'asdf')) + expect_snapshot_failure(expect_match(one, 'asdf', fixed = TRUE)) +}) + +test_that("useful failure messages for vectors", { + local_reproducible_output(unicode = TRUE) many <- c("a", "a", "b") expect_snapshot_failure(expect_match(many, "a")) diff --git a/tests/testthat/test-expect-named.R b/tests/testthat/test-expect-named.R index c25bedc69..21dfe4aed 100644 --- a/tests/testthat/test-expect-named.R +++ b/tests/testthat/test-expect-named.R @@ -1,45 +1,34 @@ test_that("expected_named verifies presence of names", { expect_success(expect_named(c(a = 1))) - expect_failure(expect_named(1:10)) + + x <- 1:10 + expect_snapshot_failure(expect_named(x)) }) test_that("expected_named verifies actual of names", { expect_success(expect_named(c(a = 1), "a")) - expect_failure(expect_named(c(a = 1), "b")) -}) -test_that("expected_named optionally ignores case", { - expect_success(expect_named(c(a = 1), "A", ignore.case = TRUE)) + x <- c(a = 1) + expect_snapshot_failure(expect_named(x, "b")) }) -test_that("expected_named optionally ignores order", { - expect_success(expect_named( - c(a = 1, b = 2), - c("b", "a"), - ignore.order = TRUE - )) +test_that("expected_named optionally ignores order and case", { + x <- c(a = 1, b = 2) + expect_success(expect_named(x, c("A", "B"), ignore.case = TRUE)) + expect_success(expect_named(x, c("b", "a"), ignore.order = TRUE)) }) test_that("provide useful feedback on failure", { - expect_snapshot_error( - expect_named(c(a = 1), c("a", "b"), ignore.order = TRUE) - ) - expect_snapshot_error( - expect_named(c(a = 1, b = 1), c("a"), ignore.order = TRUE) - ) - expect_snapshot_error( - expect_named(c(a = 1), c("b"), ignore.order = TRUE) - ) + x1 <- c(a = 1) + x2 <- c(a = 1, b = 2) - expect_snapshot_error( - expect_named(c(a = 1), c("a", "b"), ignore.order = FALSE) - ) - expect_snapshot_error( - expect_named(c(a = 1, b = 1), c("a"), ignore.order = FALSE) - ) - expect_snapshot_error( - expect_named(c(a = 1), c("b"), ignore.order = FALSE) - ) + expect_snapshot_failure(expect_named(x1, c("a", "b"), ignore.order = TRUE)) + expect_snapshot_failure(expect_named(x2, "a", ignore.order = TRUE)) + expect_snapshot_failure(expect_named(x1, "b", ignore.order = TRUE)) + + expect_snapshot_failure(expect_named(x1, c("a", "b"), ignore.order = FALSE)) + expect_snapshot_failure(expect_named(x2, "a", ignore.order = FALSE)) + expect_snapshot_failure(expect_named(x1, c("b"), ignore.order = FALSE)) }) test_that("expect_named validates its inputs", { diff --git a/tests/testthat/test-expect-no-condition.R b/tests/testthat/test-expect-no-condition.R index fbfbb7af8..eb9a525f2 100644 --- a/tests/testthat/test-expect-no-condition.R +++ b/tests/testthat/test-expect-no-condition.R @@ -29,7 +29,7 @@ test_that("expect_no_ continues execution", { }) test_that("expect_no_* don't emit success when they fail", { - expect_failure(expect_no_error(stop("!"))) + expect_snapshot_failure(expect_no_error(stop("!"))) }) test_that("capture correct trace_env (#1994)", { diff --git a/tests/testthat/test-expect-output.R b/tests/testthat/test-expect-output.R index 7eea1f901..c98144463 100644 --- a/tests/testthat/test-expect-output.R +++ b/tests/testthat/test-expect-output.R @@ -3,11 +3,11 @@ g <- function() cat("!") test_that("expect = NA checks for no output", { expect_success(expect_output(f(), NA)) - expect_failure(expect_output(g(), NA), "produced output") + expect_snapshot_failure(expect_output(g(), NA)) }) test_that("expect = NULL checks for some output", { - expect_failure(expect_output(f(), NULL), "produced no output") + expect_snapshot_failure(expect_output(f(), NULL)) expect_success(expect_output(g(), NULL)) }) diff --git a/tests/testthat/test-expect-reference.R b/tests/testthat/test-expect-reference.R index b71a620e2..1e804beeb 100644 --- a/tests/testthat/test-expect-reference.R +++ b/tests/testthat/test-expect-reference.R @@ -2,7 +2,7 @@ test_that("succeeds only when same object", { local_edition(2) x <- y <- 1 expect_success(expect_reference(x, y)) - expect_failure(expect_reference(x, 1)) + expect_snapshot_failure(expect_reference(x, 1)) }) test_that("returns value", { diff --git a/tests/testthat/test-expect-self-test.R b/tests/testthat/test-expect-self-test.R index 5eb2820e0..4c14bd5e4 100644 --- a/tests/testthat/test-expect-self-test.R +++ b/tests/testthat/test-expect-self-test.R @@ -19,8 +19,26 @@ test_that("expect_failure() requires 1 failure and zero successes", { test_that("expect_failure() can optionally match message", { expect_success(expect_failure(fail("apple"), "apple")) expect_failure(expect_failure(fail("apple"), "banana")) +}) + +test_that("expect_failure() generates a useful error messages", { + expect_no_failure <- function() {} + expect_many_failures <- function() { + fail() + fail() + } + expect_has_success <- function() { + fail() + pass(NULL) + } + expect_failure_foo <- function() fail("foo") - expect_snapshot_failure(expect_failure(fail("apple"), "banana")) + expect_snapshot_failure({ + expect_failure(expect_no_failure()) + expect_failure(expect_many_failures()) + expect_failure(expect_has_success()) + expect_failure(expect_failure_foo(), "bar") + }) }) test_that("expect_success() requires 1 success and zero failures", { @@ -47,6 +65,25 @@ test_that("show_failure", { expect_snapshot(show_failure(expect_true(FALSE))) }) + +test_that("expect_success() generates a useful error messages", { + expect_no_success <- function() {} + expect_many_successes <- function() { + pass(NULL) + pass(NULL) + } + expect_has_failure <- function() { + fail() + pass(NULL) + } + + expect_snapshot_failure({ + expect_success(expect_no_success()) + expect_success(expect_many_successes()) + expect_success(expect_has_failure()) + }) +}) + test_that("can count successes and failures", { status <- capture_success_failure({}) expect_equal(status$n_success, 0) diff --git a/tests/testthat/test-expect-setequal.R b/tests/testthat/test-expect-setequal.R index 4dd41b0de..45b031a04 100644 --- a/tests/testthat/test-expect-setequal.R +++ b/tests/testthat/test-expect-setequal.R @@ -35,16 +35,32 @@ test_that("checks inputs", { test_that("useful message on failure", { expect_snapshot_failure(expect_setequal("actual", "expected")) - expect_snapshot_failure(expect_setequal(1:2, 2)) - expect_snapshot_failure(expect_setequal(2, 2:3)) - expect_snapshot_failure(expect_setequal(1:2, 2:3)) + x <- 1:2 + y <- 2 + expect_snapshot_failure(expect_setequal(x, y)) + + x <- 2 + y <- 2:3 + expect_snapshot_failure(expect_setequal(x, y)) + + x <- 1:2 + y <- 2:3 + expect_snapshot_failure(expect_setequal(x, y)) # doesn't repeat values - expect_snapshot_failure(expect_setequal(c("a", "a"), c("b", "b", "b"))) + x <- c("a", "a") + y <- c("b", "b", "b") + expect_snapshot_failure(expect_setequal(x, y)) + + # still looks good when expected is inlined + x <- c("a", "b", "c") + expect_snapshot_failure(expect_setequal(x, c("a", "b", "c", "d"))) }) test_that("truncates long vectors", { - expect_snapshot_failure(expect_setequal(1:2, 1:50)) + x <- 1:2 + y <- 1:50 + expect_snapshot_failure(expect_setequal(x, y)) }) # mapequal ---------------------------------------------------------------- @@ -59,9 +75,15 @@ test_that("ignores order recursively", { expect_success(expect_mapequal(x, y)) }) -test_that("error if any names are duplicated", { - 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))) +test_that("fails when any names are duplicated", { + 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) @@ -78,12 +100,18 @@ test_that("fail if names don't match", { }) test_that("fails if values don't match", { - expect_failure(expect_mapequal(list(a = 1, b = 2), list(a = 1, b = 3))) + expect_failure(expect_mapequal( + list(a = 1, b = 2), + list(a = 1, b = 3) + )) }) 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))) + expect_failure(expect_mapequal( + list(1, b = 2, c = 3), + list(b = 2, 1, c = 3) + )) }) # contains ---------------------------------------------------------------- diff --git a/tests/testthat/test-expect-shape.R b/tests/testthat/test-expect-shape.R index 4b1742879..ce0de5a11 100644 --- a/tests/testthat/test-expect-shape.R +++ b/tests/testthat/test-expect-shape.R @@ -1,10 +1,14 @@ test_that("length computed correctly", { expect_success(expect_length(1, 1)) - expect_failure(expect_length(1, 2), "has length 1, not length 2.") expect_success(expect_length(1:10, 10)) expect_success(expect_length(letters[1:5], 5)) }) +test_that("generates actionable failure message", { + x <- 1:10 + expect_snapshot_failure(expect_length(x, 2)) +}) + test_that("uses S4 length method", { A <- setClass("ExpectLengthA", slots = c(x = "numeric", y = "numeric")) setMethod("length", "ExpectLengthA", function(x) 5L) diff --git a/tests/testthat/test-expect-silent.R b/tests/testthat/test-expect-silent.R index 2c3e0c7d7..47be38c54 100644 --- a/tests/testthat/test-expect-silent.R +++ b/tests/testthat/test-expect-silent.R @@ -6,6 +6,15 @@ test_that("checks for any type of output", { expect_success(expect_silent("")) }) +test_that("generates useful failure message", { + f <- function() { + warning("warning") + message("message") + cat("output") + } + expect_snapshot_failure(expect_silent(f())) +}) + test_that("returns first argument", { expect_equal(expect_silent(1), 1) }) diff --git a/tests/testthat/test-quasi-label.R b/tests/testthat/test-quasi-label.R index 2dab0f8b0..501013be9 100644 --- a/tests/testthat/test-quasi-label.R +++ b/tests/testthat/test-quasi-label.R @@ -10,6 +10,30 @@ test_that("symbols are quoted", { expect_equal(expr_label(quote(a)), "`a`") }) + +test_that("is_call_infix() handles complex calls (#1472)", { + expect_false(is_call_infix(quote( + base::any( + c( + veryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy_long_name = TRUE + ), + na.rm = TRUE + ) + ))) + + withr::local_envvar( + "_R_CHECK_LENGTH_1_LOGIC2_" = "TRUE", + ) + expect_true( + base::any( + c( + veryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy_long_name = TRUE + ), + na.rm = TRUE + ) + ) +}) + test_that("long vectors get ...", { long <- "123456789_123456789_123456789_123456789_123456789_123456789_" expect_equal( @@ -60,7 +84,7 @@ test_that("labelling compound {} expression gives single string", { test_that("can label multiline functions", { expect_equal( expr_label(quote(function(x, y) {})), - "function(x, y) ..." + "`function(x, y) ...`" ) }) diff --git a/tests/testthat/test-snapshot-file.R b/tests/testthat/test-snapshot-file.R index 302adf3c5..493f9be39 100644 --- a/tests/testthat/test-snapshot-file.R +++ b/tests/testthat/test-snapshot-file.R @@ -71,7 +71,7 @@ test_that("basic workflow", { # fails if changed snapper$start_file("snapshot-6", "test") path2 <- write_tmp_lines(letters[-1]) - expect_failure(expect_snapshot_file(path2, "letters.txt")) + expect_failure(expect_snapshot_file(path2, "letters.txt"), "has changed") snapper$end_file() }) diff --git a/tests/testthat/test-snapshot-reporter.R b/tests/testthat/test-snapshot-reporter.R index fbdeb55da..c990b397a 100644 --- a/tests/testthat/test-snapshot-reporter.R +++ b/tests/testthat/test-snapshot-reporter.R @@ -184,7 +184,7 @@ test_that("`expect_error()` can fail inside `expect_snapshot()`", { reporter = NULL ) err <- out[[1]]$results[[1]] - expect_match(err$message, "did not throw the expected error") + expect_snapshot(err$message) }) diff --git a/tests/testthat/test-snapshot.R b/tests/testthat/test-snapshot.R index ebed7c7e3..b714dfd2d 100644 --- a/tests/testthat/test-snapshot.R +++ b/tests/testthat/test-snapshot.R @@ -66,7 +66,7 @@ test_that("can scrub output/messages/warnings/errors", { test_that("always checks error status", { expect_error(expect_snapshot(stop("!"), error = FALSE)) - expect_failure(expect_snapshot(print("!"), error = TRUE)) + expect_snapshot_failure(expect_snapshot(print("!"), error = TRUE)) }) test_that("can capture error/warning messages", { diff --git a/vignettes/custom-expectation.Rmd b/vignettes/custom-expectation.Rmd index ca786a9ef..0b50083fe 100644 --- a/vignettes/custom-expectation.Rmd +++ b/vignettes/custom-expectation.Rmd @@ -30,7 +30,10 @@ expect_length <- function(object, n) { # 2. Check if expectations are violated act_n <- length(act$val) if (act_n != n) { - msg <- sprintf("%s has length %i, not length %i.", act$lab, act_n, n) + msg <- c( + sprintf("Expected %s to have length %i.", act$lab, n), + sprintf("Actual length: %i.", act$n) + ) return(fail(msg)) } @@ -41,7 +44,7 @@ expect_length <- function(object, n) { The first step in any expectation is to use `quasi_label()` to capture a "labelled value", i.e. a list that contains both the value (`$val`) for testing and a label (`$lab`) for messaging. This is a pattern that exists for fairly esoteric reasons; you don't need to understand it, just copy and paste it 🙂. -Next you need to check each way that `object` could violate the expectation. In this case, there's only one check, but in more complicated cases there can be multiple checks. In most cases, it's easier to check for violations one by one, using early returns to `fail()`. This makes it easier to write informative failure messages that state both what the object is and what you expected. +Next you need to check each way that `object` could violate the expectation. In this case, there's only one check, but in more complicated cases there can be multiple checks. In most cases, it's easier to check for violations one by one, using early returns to `fail()`. This makes it easier to write informative failure messages that first describe what was expected and then what was actually seen. Also note that you need to use `return(fail())` here. You won't see the problem when interactively testing your function because when run outside of `test_that()`, `fail()` throws an error, causing the function to terminate early. When running inside of `test_that()`, however, `fail()` does not stop execution because we want to collect all failures in a given test. @@ -98,13 +101,19 @@ expect_vector_length <- function(object, n) { # It's non-trivial to check if an object is a vector in base R so we # use an rlang helper if (!rlang::is_vector(act$val)) { - msg <- sprintf("%s is a %s, not a vector", act$lab, typeof(act$val)) + msg <- c( + sprintf("Expected %s to be a vector", act$lab), + sprintf("Actual type: %s", typeof(act$val)) + ) return(fail(msg)) } act_n <- length(act$val) if (act_n != n) { - msg <- sprintf("%s has length %i, not length %i.", act$lab, act_n, n) + msg <- c( + sprintf("Expected %s to have length %i.", act$lab, n), + sprintf("Actual length: %i.", act_n) + ) return(fail(msg)) } @@ -131,19 +140,22 @@ expect_s3_class <- function(object, class) { act <- quasi_label(rlang::enquo(object)) if (!is.object(act$val)) { - return(fail(sprintf("%s is not an object.", act$lab))) + msg <- sprintf("Expected %s to be an object.", act$lab) + return(fail(msg)) } if (isS4(act$val)) { - return(fail(sprintf("%s is an S4 object, not an S3 object.", act$lab))) + msg <- c( + sprintf("Expected %s to be an S3 object.", act$lab), + "Actual OO type: S4" + ) + return(fail(msg)) } if (!inherits(act$val, class)) { - msg <- sprintf( - "%s inherits from %s not %s.", - act$lab, - paste0(class(object), collapse = "/"), - paste0(class, collapse = "/") + msg <- c( + sprintf("Expected %s to inherit from %s.", act$lab, class), + sprintf("Actual class: %s", class(act$val)) ) return(fail(msg)) } @@ -165,7 +177,13 @@ expect_s3_class(x3, "integer") expect_s3_class(x3, "factor") ``` -Note that I also check that the `class` argument must be a string. This is an error, not a failure, because it suggests you're using the function incorrectly. +Note the variety of messages: + +* When `object` isn't an object, we only need to say what we expect. +* When `object` isn't an S3 object, we know it's an S4 object. +* When `inherits()` is `FALSE`, we provide the actual _class_, since that's most informative. + +I also check that the `class` argument must be a string. This is an error, not a failure, because it suggests you're using the function incorrectly. ```{r} #| error: true