1
2#' Expectation: does the given call match the expected?
3#'
4#' Together with \code{\link{mock}} can be used to verify whether the
5#' call expression (\code{\link{expect_call}}) and/or argument values
6#' (\code{\link{expect_args}}) match the expected.
7#'
8#' With \code{expect_called} you can check how many times has the mock
9#' object been called.
10#'
11#' @param mock_object A \code{\link{mock}} object.
12#' @param n Call number or total number of calls.
13#' @param expected_call Expected call expression; will be compared unevaluated.
14#' @param ... Arguments as passed in a call.
15#'
16#' @examples
17#' library(testthat)
18#'
19#' # expect call expression (signature)
20#' m <- mock()
21#' with_mock(summary = m, summary(iris))
22#'
23#' # it has been called once
24#' expect_called(m, 1)
25#'
26#' # the first (and only) call's arguments matches summary(iris)
27#' expect_call(m, 1, summary(iris))
28#'
29#' # expect argument value
30#' m <- mock()
31#' a <- iris
32#' with_mock(summary = m, summary(object = a))
33#' expect_args(m, 1, object = a)
34#' # is an equivalent to ...
35#' expect_equal(mock_args(m)[[1]], list(object = a))
36#'
37#' @name call-expectations
38#'
39NULL
40
41
42
43#' @export
44#' @rdname call-expectations
45#'
46#' @importFrom testthat expect
47expect_call <- function (mock_object, n, expected_call) {
48  stopifnot(is_mock(mock_object))
49
50  expect(
51    0 < n && n <= length(mock_object),
52    sprintf("call number %s not found in mock object", toString(n))
53  )
54
55  expected_call <- substitute(expected_call)
56  mocked_call <- mock_calls(mock_object)[[n]]
57
58  format_call <- function (x) paste(trimws(deparse(x)), collapse = ' ')
59
60  expect(
61    identical(mocked_call, expected_call),
62    sprintf("expected call %s does not mach actual call %s.",
63            format_call(expected_call), format_call(mocked_call))
64  )
65
66  invisible(TRUE)
67}
68
69
70#' @export
71#' @rdname call-expectations
72#'
73#' @importFrom testthat expect expect_equal
74expect_args <- function (mock_object, n, ...)
75{
76  stopifnot(is_mock(mock_object))
77
78  expect(
79    0 < n && n <= length(mock_object),
80    sprintf("arguments list number %s not found in mock object", toString(n))
81  )
82
83  expected_args <- list(...)
84  actual_args   <- mock_args(mock_object)[[n]]
85
86  expect_equal(length(actual_args), length(expected_args),
87               info = 'number of expected args does not match the actual')
88
89  for (i in seq_along(actual_args)) {
90    expect_equal(
91      actual_args[[i]],
92      expected_args[[i]],
93      label = paste(ordinal(i), 'actual argument'),
94      expected.label = paste(ordinal(i), 'expected argument')
95    )
96  }
97
98  invisible(TRUE)
99}
100
101
102ordinal <- function (x)
103{
104  stopifnot(is.integer(x), x > 0, length(x) == 1)
105  if (x %in% 11:13)
106    return(paste0(x, 'th'))
107
108  as_string  <- as.character(x)
109  last_digit <- substring(as_string, nchar(as_string))
110  suffix <- switch(last_digit,
111         `1` = 'st',
112         `2` = 'nd',
113         `3` = 'rd',
114               'th')
115  paste0(as_string, suffix)
116}
117
118
119#' @export
120#' @rdname call-expectations
121expect_called <- function (mock_object, n)
122{
123  stopifnot(is_mock(mock_object))
124
125  expect(
126    length(mock_object) == n,
127    sprintf("mock object has not been called %s time%s", toString(n),
128            (if(n>1) "s" else ""))
129  )
130}
131