Xpectr
R package for generating expectations for `testthat` unit testing
Install / Use
/learn @LudvigOlsen/XpectrREADME
xpectr
<!-- badges: start --> <!-- badges: end -->xpectr provides a set of utilities and RStudio addins for generating
tests for testthat unit testing.
Author: Ludvig R. Olsen ( r-pkgs@ludvigolsen.dk ) <br/> Started: January 2020
When developing R packages, it’s good practice to build a good set of
unit and regression tests that will notify you when something breaks in
the future. For this, the testthat
package is commonly used. Often though, we end up writing a similar set
of tests again and again, which can be both tedious and time-consuming.
xpectr helps you by generating common testthat tests. Its goal is
not to replace the active thinking process of finding meaningful
tests for your functions, but to help systematize and ease that
process.
One such example is gxs_function(), which generates tests for a set of
argument value combinations. This allows you to focus on the selection
of argument values to test. Once the tests have been generated, you can
go through each of them to ensure your function is working as intended
and to add relevant tests.
Note: If you comment out the call to gxs_function(), it is easy to
later regenerate the tests. By using a diff tool, you can check that
only the intended changes have been made to the file.
Installation
Install the CRAN version with:
install.packages("xpectr")
Install the development version with:
# install.packages("devtools")
devtools::install_github("ludvigolsen/xpectr")
Main functions
Generator functions
These functions are used to generate expectations (gxs).
| Function | Description |
|:---|:---|
| gxs_selection() | Generates testthat::expect_* statements from a selection (string of code) |
| gxs_function() | Generates testthat::expect_* statements for combinations of supplied argument values |
Functions for use in tests
| Function | Description |
|:---|:---|
| strip() | Strips strings of non-alphanumeric characters |
| strip_msg() | Strips side-effect messages of non-alphanumeric characters and rethrows them |
| suppress_mw() | Suppresses warnings and messages |
| capture_side_effects() | Captures errors, warnings, and messages from an expression |
| smpl() | Samples a vector, factor or data frame with internal random seed |
| simplified_formals() | Formats formals as easily testable character vector |
| element_lengths(), element_types(), element_classes() | Gets the length/type/class of each element |
| num_total_elements() | Unlists recursively and finds the total number of elements |
| set_test_seed() | Set random seed for unit tests compatible with R < 3.6.0 |
Helper functions
| Function | Description |
|:---|:---|
| prepare_insertion() | Collapses a vector of expectation strings and adds indentation |
| capture_parse_eval_side_effects() | Wraps string in capture_side_effects() before parsing and evaluating it |
| stop_if(), warn_if(), message_if() | If TRUE, generate error/warning/message with the supplied message |
Addins
| Addin | Description | Suggested Key Command |
|:---|:---|:---|
| Insert Expectations </br><font size="2">insertExpectationsAddin()</font> | Generates testthat expect_* tests from selected code (with gxs_selection()) | Alt+E |
| Initialize test_that() </br><font size="2">initializeTestthatAddin()</font> | Inserts testthat::test_that() code | Alt+T |
| Initialize gxs_function() </br><font size="2">initializeGXSFunctionAddin()</font> | Initializes a gxs_function() call with default values of a function | Alt+F |
| dput() selected </br><font size="2">dputSelectedAddin()</font> | Applies dput() to selected code | Alt+D |
| Wrap string with paste0() </br><font size="2">wrapStringAddin()</font> | Splits selected string every n characters and wraps in paste0() call | Alt+P |
| Insert checkmate AssertCollectioncode </br><font size="2">assertCollectionAddin()</font> | Inserts code for initializing and reporting a checkmate AssertCollection | Alt+C |
| Navigate To Test File </br><font size="2">navigateTestFileAddin()</font> | Navigates to extracted file name and line number. E.g. select or copy test_x.R:5 and it opens /tests/testthat/test_x.R at line 5. | Alt+N |
Using in packages
Suggestion: Add xpectr in the Suggests field in the DESCRIPTION
file.
Table of Contents
- xpectr
Examples
library(xpectr)
library(testthat)
library(dplyr)
# Set a seed
# When R > 3.6.0, it sets sampling.kind to "Rounding" to make
# tests compatible with previous versions of R
set_test_seed(42)
# Some data
num_vec <- 1:10
long_vec <- c(LETTERS, letters)
a_factor <- factor(c("a","b","c"))
df <- data.frame(
'a' = c(1, 2, 3),
'b' = c('t', 'y', 'u'),
"c" = a_factor,
stringsAsFactors = FALSE
) %>%
dplyr::group_by(a)
# A function with side effects
fn <- function(raise = FALSE){
message("Hi! I'm Kevin, your favorite message!")
warning("G'Day Mam! I'm a warning to the world!")
message("Kevin is ma name! Yesss!")
warning("Hopefully the whole world will see me :o")
if (isTRUE(raise)){
stop("Lord Evil Error has arrived! Yeehaaa")
}
"the output"
}
gxs_selection
Note: gxs_selection() can be used with the Insert Expectations
addin. See ?insertExpectationsAddin for instructions on how to set up
a key command.
Selection is a vector
Numeric vector
# Inspect num_vec
num_vec
#> [1] 1 2 3 4 5 6 7 8 9 10
# Generate expectations
gxs_selection("num_vec")
# Inserts the following tests:
## Testing 'num_vec' ####
## Initially generated by xpectr
xpectr::set_test_seed(42)
# Testing class
expect_equal(
class(num_vec),
"integer",
fixed = TRUE)
# Testing type
expect_type(
num_vec,
type = "integer")
# Testing values
expect_equal(
num_vec,
c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
tolerance = 1e-4)
# Testing names
expect_equal(
names(num_vec),
NULL,
fixed = TRUE)
# Testing length
expect_equal(
length(num_vec),
10L)
# Testing sum of element lengths
expect_equal(
sum(xpectr::element_lengths(num_vec)),
10L)
## Finished testing 'num_vec' ####
Factor
# Inspect a_factor
a_factor
#> [1] a b c
#> Levels: a b c
# Generate expectations
gxs_selection("a_factor")
# Inserts the following tests:
## Testing 'a_factor' ####
## Initially generated by xpectr
xpectr::set_test_seed(42)
# Testing is factor
expect_true(
is.factor(a_factor))
# Testing values
expect_equal(
as.character(a_factor),
c("a", "b", "c"),
fixed = TRUE)
# Testing names
expect_equal(
names(a_factor),
NULL,
fixed = TRUE)
# Testing length
expect_equal(
length(a_factor),
3L)
# Testing number of levels
expect_equal(
nlevels(a_factor),
3L)
# Testing levels
expect_equal(
levels(a_factor),
c("a", "b", "c"),
fixed = TRUE)
## Finished testing 'a_factor' ####
Long vector (sampling)
By default, vectors with more than 30 elements will be sampled. This
adds smpl(), which temporarily sets a seed to make sure the same
elements are returned every time.
# Inspect long_vec
long_vec
#> [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "
Related Skills
node-connect
342.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
85.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
342.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
342.5kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
