-
Notifications
You must be signed in to change notification settings - Fork 0
/
create_custom_assertions.Rmd
133 lines (88 loc) · 4.35 KB
/
create_custom_assertions.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
---
title: "Create Custom Assertions"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{create_custom_assertions}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```
## Getting Started
Do you have a custom assertion that you want to use repeatedly in your code? Then create your own assertion functions with the `assert_create()` function!
Lets start by recreating the `assert_numeric()` assertion using `assert_create()`:
```{r, eval=FALSE}
# Load library
library(assertions)
# Create a function that asserts input is numeric
assert_numeric_2 <- assert_create(
func = is.numeric, # Returns TRUE/FALSE for assertion PASS/FAIL
default_error_msg = "'{arg_name}' must be of type numeric, not {class(arg_value)}"
)
# Assertion passes if input is numeric
assert_numeric_2(123)
# But throws the expected error if input is not numeric
assert_numeric_2("abc")
```
To create a custom assertion function, you need to supply two arguments to assert_create():
`func`: a **function** that take an object to assert and returns TRUE or FALSE depending on whether the assertion should **pass** or **fail**.
`default_error_msg`: a **character string** providing an **error message** in case the assertion **fails**. This string can include special termss such as:
1. **{arg_name}** to refer to the name of the variable being checked
2. **{arg_value}** to refer to the value of the variable.
3. **{code_to_evaluate}** to evaluate code within the error message. Customise '*code_to_evaluate*'. e.g `{class(arg_name)}`
4. **{.strong bold_text}** to perform inline formatting. Customise '*bold_text*'
## Advanced Assertions
**Problem:**
Sometimes it is necessary to perform several assertions on a single object, and return error messages specific to the mode of failure. In these cases, it can be useful to chain together a series of different assertions on the object.
**Solution:**
The `assert_create_chain()` function allows you to combine multiple assertion functions created with `assert_create()` into a single assertion. Each of the wrapped assertions are evaluated in the order they are supplied, and if any of the assertions fail, the appropriate error message is returned.
**Example**
Here's an example of how to use `assert_create_chain()` to create an assertion function that asserts input is a string by individually asserting that
1. Input is a 'character' type
2. Input length is 1
```{r, eval=FALSE}
assert_string <- assert_create_chain(
assert_create(is.character, '{arg_name} must be a character, not {class(arg_value)}'),
assert_create(function(s){ length(s)==1 }, '{arg_name} must be length 1, not {arg_value}')
)
# Assert String
assert_string("String")
assert_string(3)
# Output: Error: '3' must be a character
```
## More Advanced Assertions
**Problem:**
We often need error messages to vary significantly based on the input.
**Solution**
In such cases, it is more convenient to define the error messages in the function you pass to `func`.
How does this work?
In our call to `assert_create`, instead of defining a `default_error_msg`, we can design `func` to return a string when the assertion should fail. This string will become the error message. By returning different strings upon different failure conditions, we can produce very diverse error messages very easily.
**Example**
Here's a recreation of the example above, using a `func` that supplies strings to indicate assertion failure
```{r, eval=FALSE}
# Define Function
is_a_string <- function(object){
if(!is.character(object))
return("{arg_name} must be a character, not {class(arg_value)}")
if(length(object) != 1){
return("{arg_name} must be length 1, not {length(object)}")
}
return(TRUE)
}
# Create Assertion
assert_is_string <- assert_create(
is_a_string
)
# Test assertion works
assert_is_string("String")
assert_is_string(3)
# 3 must be a character, not numeric
assert_is_string(c("A", "B"))
```
**Additional Notes**
Note that in your error strings can use the special terms such as `{arg_name}`, but you will NOT have access to the first argument using its original name (e.g. `{object}`, in the example above). This is because assert_create changes this first arguments name.
Values of all other arguments can be referred to in string using `{name_of_nonfirst_argument}`