SkillAgentSearch skills...

Xpectr

R package for generating expectations for `testthat` unit testing

Install / Use

/learn @LudvigOlsen/Xpectr
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<!-- README.md is generated from README.Rmd. Please edit that file -->

xpectr

<!-- badges: start -->

CRAN
status metacran
downloads minimal R
version Codecov test
coverage GitHub Actions CI
status AppVeyor build
status DOI

<!-- 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

Lifecycle:
experimental

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

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

View on GitHub
GitHub Stars37
CategoryDevelopment
Updated3mo ago
Forks1

Languages

R

Security Score

72/100

Audited on Dec 6, 2025

No findings