patrick/0000755000176200001440000000000015113521612011704 5ustar liggesuserspatrick/tests/0000755000176200001440000000000015113412002013036 5ustar liggesuserspatrick/tests/testthat/0000755000176200001440000000000015113414372014712 5ustar liggesuserspatrick/tests/testthat/test-with_parameters.R0000644000176200001440000001377215113414372021222 0ustar liggesusers# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. with_parameters_test_that( "Running tests:", { if (test_outcome == "success") { testthat::expect_success(testthat::expect_true(case)) } else { failure_message <- "`case` (isn't true|is not TRUE|to be TRUE)" testthat::expect_failure(testthat::expect_true(case), failure_message) } }, test_outcome = c("success", "fail", "null"), case = list(TRUE, FALSE, NULL), .test_name = c("success", "fail", "null") ) with_parameters_test_that( "Names are added", { testthat::expect_identical(.test_name, "case=TRUE") }, case = TRUE ) with_parameters_test_that( "Names can be extracted from cases", { testthat::expect_identical( .test_name, "logical=FALSE, number=1, string=hello" ) }, .cases = data.frame( logical = FALSE, number = 1.0, string = "hello", stringsAsFactors = FALSE ) ) with_parameters_test_that( "Cases are correctly evaluated:", { testthat::expect_length(vec, len) }, cases( one = list(vec = 1L, len = 1L), ten = list(vec = 1:10, len = 10L) ) ) with_parameters_test_that( "Cases are correctly evaluated with names added:", { testthat::expect_identical(.test_name, "vec=1, len=1") }, cases(list(vec = 1L, len = 1L)) ) with_parameters_test_that( "Data frames can be passed to cases:", { result <- rlang::as_function(FUN)(input) testthat::expect_identical(result, out) }, .cases = tibble::tribble( ~.test_name, ~FUN, ~input, ~out, "times", ~ .x * 2L, 2L, 4L, "plus", ~ .x + 3L, 3L, 6L ) ) with_parameters_test_that( "Patrick doesn't throw inappropriate warnings:", { testthat::expect_warning(fun(), regexp = message) }, cases( shouldnt_warn = list(fun = \() 1L + 1L, message = NA), should_warn = list( fun = \() warning("still warn!", call. = FALSE), message = "still warn" ) ) ) test_that("Patrick catches the right class of warning", { testthat::local_mocked_bindings( test_that = function(...) { rlang::warn("New warning", class = "testthat_braces_warning") } ) with_parameters_test_that( "No more warnings:", { testthat::expect_true(truth) }, truth = TRUE ) |> testthat::expect_no_warning() }) # From testthat/tests/testthat/test-test-that.R # Use for checking that line numbers are still correct expectation_lines <- function(code) { code_srcref <- attr(substitute(code), "srcref") if (!is.list(code_srcref)) { stop("code doesn't have srcref", call. = FALSE) } results <- testthat::with_reporter("silent", code)$expectations() unlist(lapply(results, \(x) x$srcref[1L])) - code_srcref[[1L]][1L] } test_that("patrick reports the correct line numbers", { # nolint start: indentation_linter. lines <- expectation_lines({ # line 1 with_parameters_test_that("simple", { # line 2 expect_true(truth) # line 3 }, # line 4 cases( true = list(truth = TRUE), false = list(truth = FALSE) )) }) # nolint end: indentation_linter. expect_identical(lines, c(3L, 3L)) }) test_that('patrick gives a deprecation warning for "test_name"', { with_parameters_test_that( "Warn about `test_name` argument:", { testthat::expect_true(truth) }, truth = TRUE, test_name = "true" ) |> testthat::expect_warning(regexp = "deprecated") with_parameters_test_that( "Warn about `test_name` column:", { testthat::expect_true(truth) }, .cases = tibble::tribble( ~test_name, ~truth, "true", TRUE ) ) |> testthat::expect_warning(regexp = "deprecated") }) expectation_names <- function(code) { expectations <- testthat::with_reporter("silent", code)$expectations() vapply(expectations, \(e) as.character(e$test), character(1L)) } test_that("glue-formatted descriptions and test names supported", { with_parameters_test_that( "testing for (x, y, z) = ({x}, {y}, {z})", { testthat::expect_gt(x + y + z, 0L) }, x = 1:10, y = 2:11, z = 3:12 ) |> expectation_names() |> expect_identical( sprintf("testing for (x, y, z) = (%d, %d, %d)", 1:10, 2:11, 3:12) ) with_parameters_test_that( "testing for (x, y, z):", { testthat::expect_gt(x + y + z, 0L) }, x = 1:10, y = 2:11, z = 3:12, .test_name = "({x}, {y}, {z})" ) |> expectation_names() |> expect_identical( sprintf("testing for (x, y, z): (%d, %d, %d)", 1:10, 2:11, 3:12) ) with_parameters_test_that( "testing for (x, y): ({x}, {y})", { testthat::expect_identical(x, y) }, x = list(NULL, 1:10), y = list(NULL, 1:10) ) |> expectation_names() |> expect_identical(sprintf( "testing for (x, y): ({x}, {y}) x=%1$s, y=%1$s", c("NULL", toString(1:10)) )) |> expect_warning("produced output of length 0") expect_warning("produced output of length 10") # but fail kindly for potential accidental use of glue # c.f. https://github.com/r-lib/lintr/issues/2706 expect_error( with_parameters_test_that("a{b}", { expect_true(TRUE) }, .cases = data.frame(d = 1L)), "Attempt to interpret test stub 'a{b}' with glue failed", fixed = TRUE ) # as well as an escape hatch to work around needing ugly escapes expect_no_error( with_parameters_test_that("a{b}", { expect_true(TRUE) }, .cases = data.frame(d = 1L), .interpret_glue = FALSE) ) }) patrick/tests/testthat.R0000644000176200001440000000117115113412002015021 0ustar liggesusers# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. library(patrick) library(testthat) test_check("patrick") patrick/MD50000644000176200001440000000102315113521612012210 0ustar liggesusers2854c640570893d26774fc3f3acd585c *DESCRIPTION 3fb4f91fce2285fd5909b47168cea716 *NAMESPACE b6c8c2c51fedfd97040bd98c7a4e8631 *NEWS.md 3f7abde6518c69756e321a1dd50b6b03 *R/patrick-package.R 32c6ecbb160ac3733f9c94950e291cfb *R/with_parameters.R cf844cdc477e0841a844b8084c74d9f8 *README.md e4a94b744bf21e12e7be4b346916b268 *man/patrick-package.Rd be939f8d5b1a4c7789587afec1b7eaa9 *man/with_parameters_test_that.Rd 7d396051ec7db71be954bcac78dcd191 *tests/testthat.R d84ac6c9ca873f9a2f77904582263500 *tests/testthat/test-with_parameters.R patrick/R/0000755000176200001440000000000015113412002012075 5ustar liggesuserspatrick/R/patrick-package.R0000644000176200001440000000300515113412002015244 0ustar liggesusers# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #' Parameterized Unit Testing #' #' `patrick` (parameterized testing in R is kind of cool!) is a `testthat` #' extension that lets you create reusable blocks of a test codes. Parameterized #' tests are often easier to read and more reliable, since they follow the DNRY #' (do not repeat yourself) rule. To do this, define tests with the function #' [with_parameters_test_that()]. Multiple approaches are provided for passing #' sets of cases. #' #' This package is inspired by parameterized testing packages in other #' languages, notably the #' [`parameterized`](https://github.com/wolever/parameterized) library in #' Python. #' @keywords internal #' @importFrom dplyr bind_rows mutate #' @importFrom glue glue_data #' @importFrom purrr modify_depth pmap pmap_chr possibly #' @importFrom rlang abort cnd_muffle enquo eval_tidy warn #' @importFrom testthat test_that #' @importFrom tibble add_column tibble #' @inherit with_parameters_test_that examples "_PACKAGE" patrick/R/with_parameters.R0000644000176200001440000001732015113412002015421 0ustar liggesusers# Copyright 2018 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #' Execute a test with parameters. #' #' This function is an extension of [testthat::test_that()] that lets you pass #' a series of testing parameters. These values are substituted into your #' regular testing code block, making it reusable and reducing duplication. #' #' You have a couple of options for passing parameters to you test. You can #' use named vectors/ lists. The function will assert that you have correct #' lengths before proceeding to test execution. Alternatively you can used #' a `data.frame` or list in combination with the splice unquote operator #' \code{\link[rlang]{!!!}}. Last, you can use the constructor `cases()`, which #' is similar to building a `data.frame` rowwise. If you manually build the #' data frame, pass it in the `.cases` argument. #' #' ## Naming test cases #' #' If the user passes a character vector as `.test_name`, each instance is #' combined with `desc_stub` to create the completed test name. Similarly, the #' named argument from `cases()` is combined with `desc_stub` to create the #' parameterized test names. When names aren't provided, they will be #' automatically generated using the test data. #' #' Names follow the pattern of "name=value, name=value" for all elements in a #' test case. #' #' @param desc_stub A string scalar. Used in creating the names of the #' parameterized tests. #' @param code Test code containing expectations. #' @param ... Named arguments of test parameters. All vectors should have the #' same length. #' @param .cases A data frame where each row contains test parameters. #' @param .test_name An alternative way for providing test names. If provided, #' the name will be appended to the stub description in `desc_stub`. If not #' provided, test names will be automatically generated. #' @param .interpret_glue Logical, default `TRUE`. If `FALSE`, and glue-like #' markup in `desc_stub` is ignored, otherwise [glue::glue_data()] is #' attempted to produce a more complete test description. #' @examples #' with_parameters_test_that("trigonometric functions match identities:", #' { #' testthat::expect_equal(expr, numeric_value) #' }, #' expr = c(sin(pi / 4), cos(pi / 4), tan(pi / 4)), #' numeric_value = c(1 / sqrt(2), 1 / sqrt(2), 1), #' .test_name = c("sin", "cos", "tan") #' ) #' #' # Run the same test with the cases() constructor #' with_parameters_test_that( #' "trigonometric functions match identities", #' { #' testthat::expect_equal(expr, numeric_value) #' }, #' cases( #' sin = list(expr = sin(pi / 4), numeric_value = 1 / sqrt(2)), #' cos = list(expr = cos(pi / 4), numeric_value = 1 / sqrt(2)), #' tan = list(expr = tan(pi / 4), numeric_value = 1) #' ) #' ) #' #' # If names aren't provided, they are automatically generated. #' with_parameters_test_that( #' "trigonometric functions match identities", #' { #' testthat::expect_equal(expr, numeric_value) #' }, #' cases( #' list(expr = sin(pi / 4), numeric_value = 1 / sqrt(2)), #' list(expr = cos(pi / 4), numeric_value = 1 / sqrt(2)), #' list(expr = tan(pi / 4), numeric_value = 1) #' ) #' ) #' # The first test case is named "expr=0.7071068, numeric_value="0.7071068" #' # and so on. #' #' # Or, pass a data frame of cases, perhaps using a helper function #' make_cases <- function() { #' tibble::tribble( #' ~.test_name, ~expr, ~numeric_value, #' "sin", sin(pi / 4), 1 / sqrt(2), #' "cos", cos(pi / 4), 1 / sqrt(2), #' "tan", tan(pi / 4), 1 #' ) #' } #' #' with_parameters_test_that( #' "trigonometric functions match identities", #' { #' testthat::expect_equal(expr, numeric_value) #' }, #' .cases = make_cases() #' ) #' @importFrom dplyr .data #' @export with_parameters_test_that <- function(desc_stub, code, ..., .cases = NULL, .test_name = NULL, .interpret_glue = TRUE) { stopifnot( is.logical(.interpret_glue), length(.interpret_glue) == 1L, !is.na(.interpret_glue) ) if (is.null(.cases)) { pars <- tibble(...) possibly_add_column <- possibly(add_column, otherwise = pars) all_pars <- possibly_add_column(pars, .test_name = .test_name) } else { all_pars <- .cases } # TODO(#33): deprecate & remove this branch if ("test_name" %in% names(all_pars)) { msg <- paste( 'The argument and cases column "test_name" is deprecated. Please use the', "new `.test_name` argument instead. See `?with_parameters_test_that`", "for more information" ) warn(msg, class = "patrick_test_name_deprecation") # It would be nicer to do this with rename(), but that function doesn't # support overwriting existing columns. all_pars <- mutate( all_pars, .test_name = .data$test_name, test_name = NULL ) } if (!".test_name" %in% names(all_pars)) { all_pars$.test_name <- build_test_names(all_pars) } captured <- enquo(code) pmap( all_pars, build_and_run_test, desc = desc_stub, code = captured, .interpret_glue = .interpret_glue ) invisible(TRUE) } #' Generate test names from cases, if none are provided. #' #' @param all_cases A tibble containing test cases. #' @return A character vector, whose length matches the number of rows in #' `all_cases`. #' @noRd build_test_names <- function(all_cases) { case_names <- names(all_cases) pmap_chr(all_cases, build_label, case_names = case_names) } build_label <- function(..., case_names) { case_row <- format(list(...)) toString(sprintf("%s=%s", case_names, case_row)) } build_description <- function(args, desc, .test_name, .interpret_glue) { if (.interpret_glue) { completed_desc <- tryCatch(glue_data(args, desc), error = identity) if (inherits(completed_desc, "error")) { abort(sprintf( paste( "Attempt to interpret test stub '%s' with glue failed with error:", "%s", "", "Set .interpret_glue=FALSE if this test name does not use glue.", sep = "\n" ), # indent for clarity (the purrr error has similar mark-up) desc, gsub("(^|\n)", "\\1 ", conditionMessage(completed_desc)) )) } } else { completed_desc <- desc } desc_n <- length(completed_desc) if (desc_n != 1L || completed_desc == desc) { completed_desc <- paste(desc, .test_name) if (desc_n != 1L) { warn( paste("glue_data() on desc= produced output of length", desc_n) ) } else if (.interpret_glue) { completed_desc <- glue_data(args, completed_desc) } } completed_desc } build_and_run_test <- function( ..., .test_name, desc, code, env, .interpret_glue ) { test_args <- list(..., .test_name = .test_name) completed_desc <- build_description(test_args, desc, .test_name, .interpret_glue) withCallingHandlers( test_that(completed_desc, eval_tidy(code, test_args)), testthat_braces_warning = cnd_muffle ) } #' @rdname with_parameters_test_that #' @export cases <- function(...) { all_cases <- list(...) nested <- modify_depth(all_cases, 2L, list) bind_rows( nested, .id = if (!is.null(names(nested))) ".test_name" ) } patrick/NAMESPACE0000644000176200001440000000101115111105617013115 0ustar liggesusers# Generated by roxygen2: do not edit by hand export(cases) export(with_parameters_test_that) importFrom(dplyr,.data) importFrom(dplyr,bind_rows) importFrom(dplyr,mutate) importFrom(glue,glue_data) importFrom(purrr,modify_depth) importFrom(purrr,pmap) importFrom(purrr,pmap_chr) importFrom(purrr,possibly) importFrom(rlang,abort) importFrom(rlang,cnd_muffle) importFrom(rlang,enquo) importFrom(rlang,eval_tidy) importFrom(rlang,warn) importFrom(testthat,test_that) importFrom(tibble,add_column) importFrom(tibble,tibble) patrick/NEWS.md0000644000176200001440000000377215113414372013017 0ustar liggesusers# patrick (0.3.1) * @chiricom is now the maintainer of {patrick}. Thanks @michaelquinn32 for 7 years at the help as package creator and maintainer! * Minor fix in the test suite to adapt to {testthat} 3.3.0. Thanks @hadley! * Require R >= 4.1.0, which {testthat} 3.3.0 does as well. # patrick (0.3.0) * Patrick can build test names as {glue}-formatted strings, e.g. ```r with_parameters_test_that( "col2hex works for color {color_name}", { expect_equal(col2hex(color_name), color_hex) }, color_name = c("red", "blue", "black"), color_hex = c("#FF0000", "#0000FF", "#000000") ) ``` This also works for supplying such a formatted string as `.test_name`. To disable this behavior, use `.interpret_glue = FALSE`. Thanks @chiricom! # patrick 0.2.0 ## New features * Patrick will try to generate names automatically if not provided. This also works when cases are provided as a data frame. # patrick 0.1.0 Breaking changes: * Setting test names should now happen with `.test_name`, instead of the implicit `test_name` variable from before. This is now an explicit argument for the function `with_parameters_test_that()`, and the leading dot should help distinguish this from values passed as cases. # patrick 0.0.4 Update `patrick` for testthat 3e. * Catch warnings for code not being braced. We still produce the right code. * Make sure patrick uses the right line numbers. # patrick 0.0.3 * Add more examples and tests for how patrick works with data frames. * Update `with_parameters_test_that()` to use [data, dots, details](https://design.tidyverse.org/dots-after-required.html#whats-the-pattern) * Modernize package files: DESCRIPTION and `R/patrick-package.R`. # patrick 0.0.2 * This is a minor update. Tests are compatible with the next version of `testthat`. # patrick 0.0.1 Welcome to `patrick`, a package for parameterizing tests within testthat. Check out the README.md file to learn more about this package. patrick/README.md0000644000176200001440000000720514534630446013203 0ustar liggesusers [![R-CMD-check](https://github.com/google/patrick/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/google/patrick/actions/workflows/R-CMD-check.yaml) [![CRAN](https://www.r-pkg.org/badges/version/patrick)](https://cran.r-project.org/package=patrick) # Introducing patrick This package is an extension to `testthat` that enables parameterized unit testing in R. ## Installing The release version of `patrick` is available on CRAN. Install it in the usual manner: ``` install.packages("patrick") ``` The development version of `patrick` is currently only available on GitHub. Install it using `devtools`. ``` devtools::install_github("google/patrick") ``` To use `patrick` as a testing tool within your package, add it to your list of `Suggests` within your package's `DESCRIPTION`. ``` Suggests: patrick ``` ## Use Many packages within R employ the following pattern when writing tests: ``` test_that("Data is a successfully converted: numeric", { input <- convert(numeric_data) expect_type(input, "double") }) test_that("Data is a successfully converted: character", { input <- convert(character_data) expect_type(input, "character") }) ``` While explicit, recycling a test pattern like this is prone to user error and other issues, as it is a violation of the classic DNRY rule (do not repeat yourself). `patrick` eliminates this problem by creating test parameters. ``` with_parameters_test_that("Data is successfully converted:", { input <- convert(test_data) expect_type(input, type) }, test_data = list(numeric_data, character_data), type = c("double", "character"), .test_name = type ) ``` Parameterized tests behave exactly the same as standard `testthat` tests. Per usual, you call all of your tests with `devtools::test`, and they'll also run during package checks. Each executes independently and then your test report will produce a single report. A complete name for each test will be formed using the initial test description and the strings in the `.test_name` parameter. Small sets of cases can be reasonably passed as parameters to `with_parameters_test_that`. This becomes less readable when the number of cases increases. To help mitigate this issue, `patrick` provides a case generator helper function. ``` with_parameters_test_that("Data is successfully converted:", { input <- convert(test_data) expect_type(input, type) }, cases( double = list(test_data = numeric_data, type = "double"), character = list(test_data = character_data, type = "character") ) ) ``` More complicated testing cases can be constructed using data frames. This is usually best handled within a helper function and in a `helper-.R` file. ``` make_cases <- function() { tibble::tribble( ~ .test_name, ~ expr, ~ numeric_value, "sin", sin(pi / 4), 1 / sqrt(2), "cos", cos(pi / 4), 1 / sqrt(2), "tan", tan(pi / 4), 1 ) } with_parameters_test_that( "trigonometric functions match identities", { testthat::expect_equal(expr, numeric_value) }, .cases = make_cases() ) ``` If you don't provide test names when generating cases, `patrick` will generate them automatically from the test data. ## Inspiration This package is inspired by parameterized testing packages in other languages, notably the [`parameterized`](https://github.com/wolever/parameterized) library in Python. ## Contributing Please read the [`CONTRIBUTING.md`](https://github.com/google/patrick/blob/master/CONTRIBUTING.md) for details on how to contribute to this project. ## Disclaimer This is not an officially supported Google product. patrick/man/0000755000176200001440000000000014757650654012505 5ustar liggesuserspatrick/man/with_parameters_test_that.Rd0000644000176200001440000000731514757650654020257 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/with_parameters.R \name{with_parameters_test_that} \alias{with_parameters_test_that} \alias{cases} \title{Execute a test with parameters.} \usage{ with_parameters_test_that( desc_stub, code, ..., .cases = NULL, .test_name = NULL, .interpret_glue = TRUE ) cases(...) } \arguments{ \item{desc_stub}{A string scalar. Used in creating the names of the parameterized tests.} \item{code}{Test code containing expectations.} \item{...}{Named arguments of test parameters. All vectors should have the same length.} \item{.cases}{A data frame where each row contains test parameters.} \item{.test_name}{An alternative way for providing test names. If provided, the name will be appended to the stub description in `desc_stub`. If not provided, test names will be automatically generated.} \item{.interpret_glue}{Logical, default `TRUE`. If `FALSE`, and glue-like markup in `desc_stub` is ignored, otherwise [glue::glue_data()] is attempted to produce a more complete test description.} } \description{ This function is an extension of [testthat::test_that()] that lets you pass a series of testing parameters. These values are substituted into your regular testing code block, making it reusable and reducing duplication. } \details{ You have a couple of options for passing parameters to you test. You can use named vectors/ lists. The function will assert that you have correct lengths before proceeding to test execution. Alternatively you can used a `data.frame` or list in combination with the splice unquote operator \code{\link[rlang]{!!!}}. Last, you can use the constructor `cases()`, which is similar to building a `data.frame` rowwise. If you manually build the data frame, pass it in the `.cases` argument. ## Naming test cases If the user passes a character vector as `.test_name`, each instance is combined with `desc_stub` to create the completed test name. Similarly, the named argument from `cases()` is combined with `desc_stub` to create the parameterized test names. When names aren't provided, they will be automatically generated using the test data. Names follow the pattern of "name=value, name=value" for all elements in a test case. } \examples{ with_parameters_test_that("trigonometric functions match identities:", { testthat::expect_equal(expr, numeric_value) }, expr = c(sin(pi / 4), cos(pi / 4), tan(pi / 4)), numeric_value = c(1 / sqrt(2), 1 / sqrt(2), 1), .test_name = c("sin", "cos", "tan") ) # Run the same test with the cases() constructor with_parameters_test_that( "trigonometric functions match identities", { testthat::expect_equal(expr, numeric_value) }, cases( sin = list(expr = sin(pi / 4), numeric_value = 1 / sqrt(2)), cos = list(expr = cos(pi / 4), numeric_value = 1 / sqrt(2)), tan = list(expr = tan(pi / 4), numeric_value = 1) ) ) # If names aren't provided, they are automatically generated. with_parameters_test_that( "trigonometric functions match identities", { testthat::expect_equal(expr, numeric_value) }, cases( list(expr = sin(pi / 4), numeric_value = 1 / sqrt(2)), list(expr = cos(pi / 4), numeric_value = 1 / sqrt(2)), list(expr = tan(pi / 4), numeric_value = 1) ) ) # The first test case is named "expr=0.7071068, numeric_value="0.7071068" # and so on. # Or, pass a data frame of cases, perhaps using a helper function make_cases <- function() { tibble::tribble( ~.test_name, ~expr, ~numeric_value, "sin", sin(pi / 4), 1 / sqrt(2), "cos", cos(pi / 4), 1 / sqrt(2), "tan", tan(pi / 4), 1 ) } with_parameters_test_that( "trigonometric functions match identities", { testthat::expect_equal(expr, numeric_value) }, .cases = make_cases() ) } patrick/man/patrick-package.Rd0000644000176200001440000000520114757650654016020 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/patrick-package.R \docType{package} \name{patrick-package} \alias{patrick} \alias{patrick-package} \title{Parameterized Unit Testing} \description{ `patrick` (parameterized testing in R is kind of cool!) is a `testthat` extension that lets you create reusable blocks of a test codes. Parameterized tests are often easier to read and more reliable, since they follow the DNRY (do not repeat yourself) rule. To do this, define tests with the function [with_parameters_test_that()]. Multiple approaches are provided for passing sets of cases. } \details{ This package is inspired by parameterized testing packages in other languages, notably the [`parameterized`](https://github.com/wolever/parameterized) library in Python. } \examples{ with_parameters_test_that("trigonometric functions match identities:", { testthat::expect_equal(expr, numeric_value) }, expr = c(sin(pi / 4), cos(pi / 4), tan(pi / 4)), numeric_value = c(1 / sqrt(2), 1 / sqrt(2), 1), .test_name = c("sin", "cos", "tan") ) # Run the same test with the cases() constructor with_parameters_test_that( "trigonometric functions match identities", { testthat::expect_equal(expr, numeric_value) }, cases( sin = list(expr = sin(pi / 4), numeric_value = 1 / sqrt(2)), cos = list(expr = cos(pi / 4), numeric_value = 1 / sqrt(2)), tan = list(expr = tan(pi / 4), numeric_value = 1) ) ) # If names aren't provided, they are automatically generated. with_parameters_test_that( "trigonometric functions match identities", { testthat::expect_equal(expr, numeric_value) }, cases( list(expr = sin(pi / 4), numeric_value = 1 / sqrt(2)), list(expr = cos(pi / 4), numeric_value = 1 / sqrt(2)), list(expr = tan(pi / 4), numeric_value = 1) ) ) # The first test case is named "expr=0.7071068, numeric_value="0.7071068" # and so on. # Or, pass a data frame of cases, perhaps using a helper function make_cases <- function() { tibble::tribble( ~.test_name, ~expr, ~numeric_value, "sin", sin(pi / 4), 1 / sqrt(2), "cos", cos(pi / 4), 1 / sqrt(2), "tan", tan(pi / 4), 1 ) } with_parameters_test_that( "trigonometric functions match identities", { testthat::expect_equal(expr, numeric_value) }, .cases = make_cases() ) } \seealso{ Useful links: \itemize{ \item \url{https://github.com/google/patrick} \item Report bugs at \url{https://github.com/google/patrick/issues} } } \author{ \strong{Maintainer}: Michael Quinn \email{msquinn@google.com} Other contributors: \itemize{ \item Michael Chirico \email{chiricom@google.com} [contributor] } } \keyword{internal} patrick/DESCRIPTION0000644000176200001440000000211515113521612013411 0ustar liggesusersPackage: patrick Title: Parameterized Unit Testing Version: 0.3.1 Authors@R: c( person(given = "Michael", family = "Quinn", role = "aut", email = "michael.quinn@aya.yale.edu"), person(given = "Michael", family = "Chirico", role = c("aut", "cre"), email = "chiricom@google.com")) Description: This is an extension of the 'testthat' package that lets you add parameters to your unit tests. Parameterized unit tests are often easier to read and more reliable, since they follow the DNRY (do not repeat yourself) rule. License: Apache License 2.0 URL: https://github.com/google/patrick BugReports: https://github.com/google/patrick/issues Depends: R (>= 4.1.0) Imports: dplyr, glue, purrr, rlang, testthat (>= 3.2.0), tibble Config/testthat/edition: 3 Encoding: UTF-8 RoxygenNote: 7.3.2 NeedsCompilation: no Repository: CRAN Packaged: 2025-12-01 22:39:28 UTC; chiricom Author: Michael Quinn [aut], Michael Chirico [aut, cre] Maintainer: Michael Chirico Date/Publication: 2025-12-02 08:30:02 UTC