The goal of validatr is to contain validation functions re-used across FRAMverse packages. This is intended for internal use only.
You can install the development version of validatr from GitHub with:
# install.packages("pak")
pak::pak("FRAMverse/validatr")Under the hood, the validation functions in validatr use a special
wrapper for cli::cli_abort() – validation_abort() – that adds an
error class of “validatr_error” to the error message. Error messages can
be accessed with the rlang::catch_cnd() function:
library(cli)
#> Warning: package 'cli' was built under R version 4.4.3
library(validatr)
normal_cli_error <- rlang::catch_cnd(
cli_abort("Normal error message!")
)
class(normal_cli_error)
#> [1] "rlang_error" "error" "condition"
framverse_cli_error <- rlang::catch_cnd(
framverse_abort("Framverse error!")
)
class(framverse_cli_error)
#> [1] "simpleError" "error" "condition"This is done to improve handling of unit and integration tests with the
testthat package. It allows us to use expect_error() and not have
false positives when something else in the function errors.
As an example: imagine we have a poorly written function foo that is
intended to take a character, validate the character, sleep for a
specified duration, and print the value to the console. Imagine we also
forgot to include the validate_character() function (here we represent
is as commented out).
foo <- function(x, delay){
# validate_character(x)
Sys.sleep(delay - 100)
print(x)
}As we’re writing unit tests, we start by writing tests of the input
validations. Does this function error when we provide a logical instead
of a character? Well, if our test causes an error elsewhere in the
function (e.g., by providing a value of delay that causes Sys.sleep to
be called on a negative number), our test will appear to be successful
even though we’re not actually doing input validation.
testthat::expect_error(foo(10, delay = 50))even though the test would throw an error if we didn’t have another problem cropping up with our function call (since the only reason we errored was that our delay value was < 100):
testthat::expect_error(foo(10, delay = 101))
#> [1] 10
#> Error: Expected `foo(10, delay = 101)` to throw a error.We can clarify this by setting the expectation for the class of error that pops up.
testthat::expect_error(foo(10, delay = 50),
class = "validatr_error")
#> Error: `foo(10, delay = 50)` threw an error with unexpected class.
#> Expected class: validatr_error
#> Actual class: simpleError/error/condition
#> Message: invalid 'time' valueThe test will only be successful if we get the correct error. Let’s
see that this works using a fixed version of our function, foo_2()
foo_2 <- function(x, delay){
validate_character(x)
Sys.sleep(delay - 100)
print(x)
}testthat::expect_error(foo_2(10, delay = 50),
class = "validatr_error")I am not intending for this to store validation functions that are
only needed for a single, specific package. For example, there are a
suite of validation functions for working with FRAM databases (e.g.,
confirm an input is a fram database, confirm that a stock id input is
present in the FRAM database). Those will not be included in this
package, as any functions that work with FRAM databases will either be
in framrsquared or will be using framrsquared functions that will do
the validation (e.g., functions in the framqaqc package).Similarly,
there are some custom validation functions in the VizSeasonalGams
package that are package-specific and will not be added here.