{"id":27655180,"url":"https://armcn.github.io/quickcheck/","last_synced_at":"2025-04-24T06:01:48.531Z","repository":{"id":44803260,"uuid":"441310729","full_name":"armcn/quickcheck","owner":"armcn","description":"Property Based Testing in R","archived":false,"fork":false,"pushed_at":"2023-10-11T22:32:00.000Z","size":3250,"stargazers_count":25,"open_issues_count":7,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-19T07:13:52.836Z","etag":null,"topics":["functional-programming","property-based-testing","r","rstats"],"latest_commit_sha":null,"homepage":"https://armcn.github.io/quickcheck","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/armcn.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-12-23T22:37:37.000Z","updated_at":"2024-10-28T23:35:49.000Z","dependencies_parsed_at":"2025-04-12T00:34:32.737Z","dependency_job_id":"178bfa5f-a58d-4234-834b-18fb5dd21292","html_url":"https://github.com/armcn/quickcheck","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/armcn%2Fquickcheck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/armcn%2Fquickcheck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/armcn%2Fquickcheck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/armcn%2Fquickcheck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/armcn","download_url":"https://codeload.github.com/armcn/quickcheck/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250573330,"owners_count":21452347,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","property-based-testing","r","rstats"],"created_at":"2025-04-24T06:01:04.136Z","updated_at":"2025-04-24T06:01:48.518Z","avatar_url":"https://github.com/armcn.png","language":"R","funding_links":[],"categories":["Generic R Packages"],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\"\n)\n```\n\n# quickcheck \u003cimg src=\"man/figures/hex.png\" align=\"right\" style=\"width: 25%;\"/\u003e\n\n\u003c!-- badges: start --\u003e\n[![CRAN status](https://www.r-pkg.org/badges/version/quickcheck)](https://CRAN.R-project.org/package=quickcheck)\n[![R-CMD-check](https://github.com/armcn/quickcheck/workflows/R-CMD-check/badge.svg)](https://github.com/armcn/quickcheck/actions)\n[![Codecov test coverage](https://codecov.io/gh/armcn/quickcheck/branch/main/graph/badge.svg)](https://app.codecov.io/gh/armcn/quickcheck?branch=main)\n[![metacran downloads](https://cranlogs.r-pkg.org/badges/quickcheck)](https://cran.r-project.org/package=quickcheck)\n\u003c!-- badges: end --\u003e\n\n# Overview\n\nProperty based testing in R, inspired by [QuickCheck](https://en.wikipedia.org/wiki/QuickCheck). This package builds on the property based testing framework provided by [`hedgehog`](https://github.com/hedgehogqa/r-hedgehog) and is designed to seamlessly integrate with [`testthat`](https://testthat.r-lib.org).\n\n## Installation\n\nYou can install the released version of `quickcheck` from [CRAN](https://CRAN.R-project.org) with:\n\n```{r, eval=FALSE}\ninstall.packages(\"quickcheck\")\n```\n\nAnd the development version from [GitHub](https://github.com/) with:\n\n```{r, eval=FALSE}\n# install.packages(\"remotes\")\nremotes::install_github(\"armcn/quickcheck\")\n```\n\n# Usage\n\nThe following example uses `quickcheck` to test the properties of the base R `+` function. [Here](https://fsharpforfunandprofit.com/posts/property-based-testing/) is an introduction to the concept of property based testing, and an explanation of the mathematical properties of addition can be found [here](https://www.khanacademy.org/math/cc-sixth-grade-math/cc-6th-factors-and-multiples/properties-of-numbers/a/properties-of-addition).\n\n```{r}\nlibrary(testthat)\nlibrary(quickcheck)\n\ntest_that(\"0 is the additive identity of +\", {\n  for_all(\n    a = numeric_(len = 1),\n    property = function(a) expect_equal(a, a + 0)\n  )\n})\n\ntest_that(\"+ is commutative\", {\n  for_all(\n    a = numeric_(len = 1),\n    b = numeric_(len = 1),\n    property = function(a, b) expect_equal(a + b, b + a)\n  )\n})\n\ntest_that(\"+ is associative\", {\n  for_all(\n    a = numeric_(len = 1),\n    b = numeric_(len = 1),\n    c = numeric_(len = 1),\n    property = function(a, b, c) expect_equal(a + (b + c), (a + b) + c)\n  )\n})\n```\n\nHere we test the properties of the [`distinct`](https://dplyr.tidyverse.org/reference/distinct.html) \nfunction from the [`dplyr`](https://dplyr.tidyverse.org/index.html) package.\n\n```{r}\nlibrary(dplyr, warn.conflicts = FALSE)\n\ntest_that(\"distinct does nothing with a single row\", {\n  for_all(\n    a = any_tibble(rows = 1L),\n    property = function(a) {\n      distinct(a) %\u003e% expect_equal(a)\n    }\n  )\n})\n\ntest_that(\"distinct returns single row if rows are repeated\", {\n  for_all(\n    a = any_tibble(rows = 1L),\n    property = function(a) {\n      bind_rows(a, a) %\u003e%\n        distinct() %\u003e%\n        expect_equal(a)\n    }\n  )\n})\n\ntest_that(\"distinct does nothing if rows are unique\", {\n  for_all(\n    a = tibble_of(integer_positive(), rows = 1L, cols = 1L),\n    b = tibble_of(integer_negative(), rows = 1L, cols = 1L),\n    property = function(a, b) {\n      unique_rows \u003c- bind_rows(a, b)\n      distinct(unique_rows) %\u003e% expect_equal(unique_rows)\n    }\n  )\n})\n```\n\n## Quickcheck generators\n\nMany generators are provided with `quickcheck`. Here are a few examples.\n\n### Atomic vectors\n\n```{r}\ninteger_(len = 10) %\u003e% show_example()\ncharacter_alphanumeric(len = 10) %\u003e% show_example()\nposixct_(len = 10, any_na = TRUE) %\u003e% show_example()\n```\n\n### Lists\n\n```{r}\nlist_(a = constant(NULL), b = any_undefined()) %\u003e% show_example()\nflat_list_of(logical_(), len = 3) %\u003e% show_example()\n```\n\n### Tibbles\n\n```{r}\ntibble_(a = date_(), b = hms_(), rows = 5) %\u003e% show_example()\ntibble_of(double_bounded(-10, 10), rows = 3, cols = 3) %\u003e% show_example()\nany_tibble(rows = 3, cols = 3) %\u003e% show_example()\n```\n\n## Hedgehog generators\n\n`quickcheck` is meant to work with `hedgehog`, not replace it. `hedgehog` generators\ncan be used by wrapping them in `from_hedgehog`. \n\n```{r}\nlibrary(hedgehog)\n\nis_even \u003c-\n  function(a) a %% 2 == 0\n\ngen_powers_of_two \u003c-\n  gen.element(1:10) %\u003e% gen.with(function(a) 2^a)\n\ntest_that(\"is_even returns TRUE for powers of two\", {\n  for_all(\n    a = from_hedgehog(gen_powers_of_two),\n    property = function(a) is_even(a) %\u003e% expect_true()\n  )\n})\n```\n\nAny `hedgehog` generator can be used with `quickcheck` but they can't be composed \ntogether to build another generator. For example this will work:\n\n```{r}\ntest_that(\"powers of two and integers are both numeric values\", {\n  for_all(\n    a = from_hedgehog(gen_powers_of_two),\n    b = integer_(),\n    property = function(a, b) {\n      c(a, b) %\u003e%\n        is.numeric() %\u003e%\n        expect_true()\n    }\n  )\n})\n```\n\nBut this will cause an error:\n\n```{r}\ntest_that(\"composing hedgehog with quickcheck generators fails\", {\n  tibble_of(from_hedgehog(gen_powers_of_two)) %\u003e% expect_error()\n})\n```\n\nA `quickcheck` generator can also be converted to a `hedgehog` generator which can \nthen be used with other `hedgehog` functions.\n\n```{r}\ngen_powers_of_two \u003c-\n  integer_bounded(1L, 10L, len = 1L) %\u003e%\n  as_hedgehog() %\u003e%\n  gen.with(function(a) 2^a)\n\n\ntest_that(\"is_even returns TRUE for powers of two\", {\n  for_all(\n    a = from_hedgehog(gen_powers_of_two),\n    property = function(a) is_even(a) %\u003e% expect_true()\n  )\n})\n```\n\n## Fuzz tests\n\nFuzz testing is a special case of property based testing in which the only \nproperty being tested is that the code doesn't fail with a range of inputs.\nHere is an example of how to do fuzz testing with `quickcheck`. Let's say we want\nto test that the `purrr::map` function won't fail with any vector as input.\n\n```{r}\ntest_that(\"map won't fail with any vector as input\", {\n  for_all(\n    a = any_vector(),\n    property = function(a) purrr::map(a, identity) %\u003e% expect_silent()\n  )\n})\n```\n\n## Repeat tests\n\nRepeat tests can be used to repeatedly test that a property holds true for many\ncalls of a function. These are different from regular property based tests\nbecause they don't require generators. The function `repeat_test` will call \na function many times to ensure the expectation passes in all cases. This kind\nof test can be useful for testing functions with randomness.\n\n```{r}\ntest_that(\"runif generates random numbers between a min and max value\", {\n  repeat_test(\n    property = function() {\n      random_number \u003c- runif(1, min = 0, max = 10)\n      expect_true(random_number \u003e= 0 \u0026\u0026 random_number \u003c= 10)\n    }\n  )\n})\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/armcn.github.io%2Fquickcheck%2F","html_url":"https://awesome.ecosyste.ms/projects/armcn.github.io%2Fquickcheck%2F","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/armcn.github.io%2Fquickcheck%2F/lists"}