A wrapper for all.equal and identical that provides more useful diagnostics when used in a unittest ok function.

ut_cmp_equal(
        a, b,
        filter = NULL,
        deparse_frame = -1,
        context_lines = getOption("unittest.cmp_context", 1e8),
        ... )

ut_cmp_identical(
        a, b,
        filter = NULL,
        deparse_frame = -1,
        context_lines = getOption("unittest.cmp_context", 1e8) )

Arguments

a

First item to compare, usually the result of whatever you are testing

b

Second item to compare, usually the expected output of whatever you are testing

filter

An optional filter function, that turns either a or b into text, and prints this out

deparse_frame

Tell sys.call which frame to deparse to get original expressions. Set to -2 when making a helper function, see examples.

context_lines

Number of lines of context surrounding changed lines to print.

...

Other arguments passed directly to all.equal

Details

For both functions, a and b are first passed to all.equal (for ut_cmp_equal()) or identical (for ut_cmp_identical()). If they match, then the function returns TRUE and your test passes.

If this fails, then we turn both a and b into text, and then use git diff to compare the 2 outputs. If you do not have git installed, then the 2 outputs will be shown side-by-side.

When using git diff, we turn colored output on when outputting to a terminal. You can force this on or off using options("cli.num_colors" = 1) or the NO_COLOR or R_CLI_NUM_COLORS environment variable.

The step of turning into text is done with the filter function. There are several of these built-in, and it will choose the one that produces the simplest output. This may mean that the output will be from the print function if the differences are obvious, or str with many decimal places if there are subtle differences between the 2.

You can also provide your own filter function if there's a particular way you would like to see the data when comparing, for example you can use write.table if your data is easiest to understand in tabular output.

Value

Returns TRUE if a & b are all.equal (for ut_cmp_equal()) or identical (for ut_cmp_identical()). Otherwise, returns an invisible() character vector of diagnostic strings helping you find where the difference is.

If called directly in an interactive R session, this output will be printed to the console.

Examples

## A function to test:
fn <- function(x) { seq(x) }

## Get it right, and test passes:
ok(ut_cmp_equal(fn(3), c(1,2,3)))
#> ok - ut_cmp_equal(fn(3), c(1, 2, 3))

## Get it wrong, and we get told where in the output things are different:
ok(ut_cmp_equal(fn(3), c(1,4,3)))
#> not ok - ut_cmp_equal(fn(3), c(1, 4, 3))
#> # Test returned non-TRUE value:
#> # Mean relative difference: 1
#> # --- fn(3)
#> # +++ c(1, 4, 3)
#> # [1] 1 [-2-]{+4+} 3

## Using a custom filter, we can format the output with write.table:
ok(ut_cmp_equal(fn(3), c(1,4,3), filter = write.table))
#> not ok - ut_cmp_equal(fn(3), c(1, 4, 3), filter = write.table)
#> # Test returned non-TRUE value:
#> # Mean relative difference: 1
#> # --- fn(3)
#> # +++ c(1, 4, 3)
#> # "x"
#> # "1" 1
#> # "2" [-2-]{+4+}
#> # "3" 3

## With ut_cmp_equal, an integer 1 is the same as a numeric 1
ok(ut_cmp_equal(as.numeric(1), as.integer(1)))
#> ok - ut_cmp_equal(as.numeric(1), as.integer(1))

## With ut_cmp_identical, they're not
ok(ut_cmp_identical(as.numeric(1), as.integer(1)))
#> not ok - ut_cmp_identical(as.numeric(1), as.integer(1))
#> # Test returned non-TRUE value:
#> # --- as.numeric(1)
#> # +++ as.integer(1)
#> #  [-num-]{+int+} 1

## all.equal() takes a tolerance parameter, for example:
all.equal(0.01, 0.02, tolerance = 0.1)
#> [1] TRUE

## ...we can also give this to to ut_cmp_equal if we want a very
## approximate comparison
ok(ut_cmp_equal(0.01, 0.02, tolerance = 0.1))
#> ok - ut_cmp_equal(0.01, 0.02, tolerance = 0.1)

## We can make a comparison function of our own, and use
## deparse_frame to show the right expression in diff output
cmp_noorder <- function (a, b) {
    sortlist <- function (x) if (length(x) > 0) x[order(names(x))] else x
    ut_cmp_identical(sortlist(a), sortlist(b), deparse_frame = -2)
}
ok(cmp_noorder(list(a=1, b=2), list(b=2, a=3)))
#> not ok - cmp_noorder(list(a = 1, b = 2), list(b = 2, a = 3))
#> # Test returned non-TRUE value:
#> # --- list(a = 1, b = 2)
#> # +++ list(b = 2, a = 3)
#> # $a
#> # [1] [-1-]{+3+}
#> # 
#> # $b
#> # [1] 2