Skip to content

Commit 66b166a

Browse files
New expectation function, expect_shape()
1 parent f8ccf03 commit 66b166a

File tree

5 files changed

+179
-4
lines changed

5 files changed

+179
-4
lines changed

NAMESPACE

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export(CheckReporter)
3535
export(CompactProgressReporter)
3636
export(DebugReporter)
3737
export(FailReporter)
38+
export(Google3Reporter)
3839
export(JunitReporter)
3940
export(ListReporter)
4041
export(LocationReporter)
@@ -49,7 +50,6 @@ export(StopReporter)
4950
export(SummaryReporter)
5051
export(TapReporter)
5152
export(TeamcityReporter)
52-
export(announce_snapshot_file)
5353
export(auto_test)
5454
export(auto_test_package)
5555
export(capture_condition)
@@ -63,8 +63,6 @@ export(capture_warning)
6363
export(capture_warnings)
6464
export(check_reporter)
6565
export(compare)
66-
export(compare_file_binary)
67-
export(compare_file_text)
6866
export(context)
6967
export(context_start_file)
7068
export(default_compact_reporter)
@@ -110,6 +108,7 @@ export(expect_reference)
110108
export(expect_s3_class)
111109
export(expect_s4_class)
112110
export(expect_setequal)
111+
export(expect_shape)
113112
export(expect_silent)
114113
export(expect_snapshot)
115114
export(expect_snapshot_error)
@@ -169,6 +168,7 @@ export(skip_on_bioc)
169168
export(skip_on_ci)
170169
export(skip_on_covr)
171170
export(skip_on_cran)
171+
export(skip_on_google)
172172
export(skip_on_os)
173173
export(skip_on_travis)
174174
export(snapshot_accept)

NEWS.md

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

3+
* New expectation, `expect_shape()`, for testing the shape (i.e., the `length()`,
4+
`nrow()` and/or `ncol()`, or `dim()`, all in one place (#1423, @michaelchirico).
5+
36
# testthat 3.1.0
47

58
## Snapshot tests

R/expect-length.R

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#' Does code return a vector with the specified length?
22
#'
3-
#' @seealso [expect_vector()] to make assertions about the "size" of a vector
3+
#' @seealso [expect_vector()] to make assertions about the "size" of a vector,
4+
#' [expect_shape()] for more general assertions about object "shape".
45
#' @inheritParams expect_that
56
#' @param n Expected length.
67
#' @family expectations
@@ -25,3 +26,84 @@ expect_length <- function(object, n) {
2526

2627
invisible(act$val)
2728
}
29+
30+
#' Does code return an object with the specified shape?
31+
#'
32+
#' By "shape", we mean an object's [dim()], or, for one-dimensional objects,
33+
#' it's [length()]. Thus this is an extension of [expect_length()] to more
34+
#' general objects like [data.frame()], [matrix()], and [array()].
35+
#' To wit, first, the object's `dim()` is checked. If non-`NULL`, it is compared
36+
#' to `shape` (or one/both of `nrow`, `ncol`, if they are supplied, in which
37+
#' case they take precedence). If `dim(object)` is `NULL`, `length(object)`
38+
#' is compared to `shape`.
39+
#'
40+
#' @seealso [expect_length()] to specifically make assertions about the
41+
#' [length()] of a vector.
42+
#' @inheritParams expect_that
43+
#' @param shape Expected shape, an integer vector.
44+
#' @family expectations
45+
#' @export
46+
#' @examples
47+
expect_shape = function(object, shape, nrow, ncol) {
48+
stopifnot(
49+
missing(shape) || is.numeric(shape),
50+
missing(nrow) || is.numeric(nrow),
51+
missing(ncol) || is.numeric(ncol)
52+
)
53+
54+
dim_object <- dim(object)
55+
if (is.null(dim_object)) {
56+
if (missing(shape)) {
57+
stop("`shape` must be provided for one-dimensional inputs")
58+
}
59+
return(expect_length(object, shape))
60+
}
61+
62+
act <- quasi_label(enquo(object), arg = "object")
63+
# testing dim
64+
if (missing(nrow) && missing(ncol)) {
65+
act$shape <- dim_object
66+
67+
expect(
68+
isTRUE(all.equal(act$shape, shape)),
69+
sprintf("%s has shape (%s), not (%s).", act$lab, toString(act$shape), toString(shape))
70+
)
71+
72+
return(act$val)
73+
}
74+
75+
# testing only ncol
76+
if (missing(nrow)) {
77+
act$ncol <- dim_object[2L]
78+
79+
expect(
80+
act$ncol == ncol,
81+
sprintf("%s has %i columns, not %i.", act$lab, act$ncol, ncol)
82+
)
83+
84+
return(act$val)
85+
}
86+
87+
# testing only nrow
88+
if (missing(ncol)) {
89+
act$nrow <- dim_object[1L]
90+
91+
expect(
92+
act$nrow == nrow,
93+
sprintf("%s has %i rows, not %i.", act$lab, act$nrow, nrow)
94+
)
95+
96+
return(act$val)
97+
}
98+
99+
# testing both nrow & ncol (useful, e.g., for testing dim(.)[1:2] for arrays
100+
act$nrow <- dim_object[1L]
101+
act$ncol <- dim_object[2L]
102+
103+
expect(
104+
act$nrow == nrow && act$ncol == ncol,
105+
sprintf("%s has %i rows and %i columns, not %i rows and %i columns", act$lab, act$nrow, act$ncol, nrow, ncol)
106+
)
107+
108+
return(act$val)
109+
}

man/expect_shape.Rd

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

tests/testthat/test-expect-shape.R

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
test_that("shape computed correctly", {
2+
# equivalent to expect_length
3+
expect_success(expect_shape(1, 1))
4+
expect_failure(expect_shape(1, 2), "has length 1, not length 2.")
5+
expect_success(expect_shape(1:10, 10))
6+
expect_success(expect_shape(letters[1:5], 5))
7+
8+
# testing dim()
9+
expect_success(expect_shape(matrix(nrow = 5, ncol = 4), c(5L, 4L)))
10+
expect_failure(expect_shape(matrix(nrow = 6, ncol = 3), c(6L, 2L)))
11+
expect_failure(expect_shape(matrix(nrow = 6, ncol = 3), c(7L, 3L)))
12+
expect_success(expect_shape(data.frame(1:10, 11:20), c(10, 2)))
13+
expect_success(expect_shape(array(dim = 1:3), 1:3))
14+
15+
# testing nrow=
16+
expect_success(expect_shape(matrix(nrow = 5, ncol = 4), nrow = 5L))
17+
expect_failure(expect_shape(matrix(nrow = 5, ncol = 5), nrow = 6L))
18+
expect_success(expect_shape(data.frame(1:10, 11:20), nrow = 10L))
19+
20+
# testing ncol=
21+
expect_success(expect_shape(matrix(nrow = 5, ncol = 4), ncol = 4L))
22+
expect_failure(expect_shape(matrix(nrow = 5, ncol = 5), ncol = 7L))
23+
expect_success(expect_shape(data.frame(1:10, 11:20), ncol = 2L))
24+
25+
# testing nrow= and ncol=
26+
expect_success(expect_shape(matrix(nrow = 5, ncol = 4), nrow = 5L, ncol = 4L))
27+
expect_failure(expect_shape(matrix(nrow = 5, ncol = 5), nrow = 6L, ncol = 5L))
28+
expect_success(expect_shape(data.frame(1:10, 11:20), nrow = 10L, ncol = 2L))
29+
expect_success(expect_shape(array(dim = 5:7), nrow = 5L, ncol = 6L))
30+
31+
# precedence of manual nrow/ncol over shape
32+
expect_success(expect_shape(matrix(nrow = 7, ncol = 10), 1:2, nrow = 7L))
33+
expect_success(expect_shape(matrix(nrow = 7, ncol = 10), 1:2, ncol = 10L))
34+
})
35+
36+
test_that("uses S4 dim method", {
37+
A <- setClass("ExpectShapeA", slots = c(x = "numeric", y = "numeric"))
38+
setMethod("dim", "ExpectShapeA", function(x) 8:10)
39+
expect_success(expect_shape(A(x = 1:9, y = 3), 8:10))
40+
})
41+
42+
test_that("returns input", {
43+
x <- list(1:10, letters)
44+
out <- expect_shape(x, 2)
45+
expect_identical(out, x)
46+
})

0 commit comments

Comments
 (0)