https://github.com/armcn/quickcheck

Property Based Testing in R

https://github.com/armcn/quickcheck

Science Score: 26.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (16.8%) to scientific vocabulary

Keywords

functional-programming property-based-testing r rstats
Last synced: 5 months ago · JSON representation

Repository

Property Based Testing in R

Basic Info
Statistics
  • Stars: 28
  • Watchers: 1
  • Forks: 1
  • Open Issues: 4
  • Releases: 4
Topics
functional-programming property-based-testing r rstats
Created about 4 years ago · Last pushed 8 months ago
Metadata Files
Readme Changelog License

README.Rmd

---
output: github_document
---



```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.path = "man/figures/README-",
  out.width = "100%"
)
```

# quickcheck 


[![CRAN status](https://www.r-pkg.org/badges/version/quickcheck)](https://CRAN.R-project.org/package=quickcheck)
[![R-CMD-check](https://github.com/armcn/quickcheck/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/armcn/quickcheck/actions/workflows/R-CMD-check.yaml)
[![Codecov test coverage](https://codecov.io/gh/armcn/quickcheck/branch/main/graph/badge.svg)](https://app.codecov.io/gh/armcn/quickcheck?branch=main)
[![metacran downloads](https://cranlogs.r-pkg.org/badges/quickcheck)](https://cran.r-project.org/package=quickcheck)


# Overview

Property 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).

## Installation

You can install the released version of `quickcheck` from [CRAN](https://CRAN.R-project.org) with:

```{r, eval=FALSE}
install.packages("quickcheck")
```

And the development version from [GitHub](https://github.com/) with:

```{r, eval=FALSE}
# install.packages("remotes")
remotes::install_github("armcn/quickcheck")
```

# Usage

The 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).

```{r}
library(testthat)
library(quickcheck)

test_that("0 is the additive identity of +", {
  for_all(
    a = numeric_(len = 1),
    property = function(a) expect_equal(a, a + 0)
  )
})

test_that("+ is commutative", {
  for_all(
    a = numeric_(len = 1),
    b = numeric_(len = 1),
    property = function(a, b) expect_equal(a + b, b + a)
  )
})

test_that("+ is associative", {
  for_all(
    a = numeric_(len = 1),
    b = numeric_(len = 1),
    c = numeric_(len = 1),
    property = function(a, b, c) expect_equal(a + (b + c), (a + b) + c)
  )
})
```

Here we test the properties of the [`distinct`](https://dplyr.tidyverse.org/reference/distinct.html) 
function from the [`dplyr`](https://dplyr.tidyverse.org/index.html) package.

```{r}
library(dplyr, warn.conflicts = FALSE)

test_that("distinct does nothing with a single row", {
  for_all(
    a = any_tibble(rows = 1L),
    property = function(a) {
      distinct(a) %>% expect_equal(a)
    }
  )
})

test_that("distinct returns single row if rows are repeated", {
  for_all(
    a = any_tibble(rows = 1L),
    property = function(a) {
      bind_rows(a, a) %>%
        distinct() %>%
        expect_equal(a)
    }
  )
})

test_that("distinct does nothing if rows are unique", {
  for_all(
    a = tibble_of(integer_positive(), rows = 1L, cols = 1L),
    b = tibble_of(integer_negative(), rows = 1L, cols = 1L),
    property = function(a, b) {
      unique_rows <- bind_rows(a, b)
      distinct(unique_rows) %>% expect_equal(unique_rows)
    }
  )
})
```

## Quickcheck generators

Many generators are provided with `quickcheck`. Here are a few examples.

### Atomic vectors

```{r}
integer_(len = 10) %>% show_example()
character_alphanumeric(len = 10) %>% show_example()
posixct_(len = 10, any_na = TRUE) %>% show_example()
```

### Lists

```{r}
list_(a = constant(NULL), b = any_undefined()) %>% show_example()
flat_list_of(logical_(), len = 3) %>% show_example()
```

### Tibbles

```{r}
tibble_(a = date_(), b = hms_(), rows = 5) %>% show_example()
tibble_of(double_bounded(-10, 10), rows = 3, cols = 3) %>% show_example()
any_tibble(rows = 3, cols = 3) %>% show_example()
```

## Hedgehog generators

`quickcheck` is meant to work with `hedgehog`, not replace it. `hedgehog` generators
can be used by wrapping them in `from_hedgehog`. 

```{r}
library(hedgehog)

is_even <-
  function(a) a %% 2 == 0

gen_powers_of_two <-
  gen.element(1:10) %>% gen.with(function(a) 2^a)

test_that("is_even returns TRUE for powers of two", {
  for_all(
    a = from_hedgehog(gen_powers_of_two),
    property = function(a) is_even(a) %>% expect_true()
  )
})
```

Any `hedgehog` generator can be used with `quickcheck` but they can't be composed 
together to build another generator. For example this will work:

```{r}
test_that("powers of two and integers are both numeric values", {
  for_all(
    a = from_hedgehog(gen_powers_of_two),
    b = integer_(),
    property = function(a, b) {
      c(a, b) %>%
        is.numeric() %>%
        expect_true()
    }
  )
})
```

But this will cause an error:

```{r}
test_that("composing hedgehog with quickcheck generators fails", {
  tibble_of(from_hedgehog(gen_powers_of_two)) %>% expect_error()
})
```

A `quickcheck` generator can also be converted to a `hedgehog` generator which can 
then be used with other `hedgehog` functions.

```{r}
gen_powers_of_two <-
  integer_bounded(1L, 10L, len = 1L) %>%
  as_hedgehog() %>%
  gen.with(function(a) 2^a)


test_that("is_even returns TRUE for powers of two", {
  for_all(
    a = from_hedgehog(gen_powers_of_two),
    property = function(a) is_even(a) %>% expect_true()
  )
})
```

## Fuzz tests

Fuzz testing is a special case of property based testing in which the only 
property being tested is that the code doesn't fail with a range of inputs.
Here is an example of how to do fuzz testing with `quickcheck`. Let's say we want
to test that the `purrr::map` function won't fail with any vector as input.

```{r}
test_that("map won't fail with any vector as input", {
  for_all(
    a = any_vector(),
    property = function(a) purrr::map(a, identity) %>% expect_silent()
  )
})
```

## Repeat tests

Repeat tests can be used to repeatedly test that a property holds true for many
calls of a function. These are different from regular property based tests
because they don't require generators. The function `repeat_test` will call 
a function many times to ensure the expectation passes in all cases. This kind
of test can be useful for testing functions with randomness.

```{r}
test_that("runif generates random numbers between a min and max value", {
  repeat_test(
    property = function() {
      random_number <- runif(1, min = 0, max = 10)
      expect_true(random_number >= 0 && random_number <= 10)
    }
  )
})
```

Owner

  • Name: Andrew R McNeil
  • Login: armcn
  • Kind: user
  • Location: Toronto, ON
  • Company: Sanofi

R Shiny Developer

GitHub Events

Total
  • Issues event: 4
  • Watch event: 1
  • Push event: 5
  • Pull request event: 1
  • Create event: 1
Last Year
  • Issues event: 4
  • Watch event: 1
  • Push event: 5
  • Pull request event: 1
  • Create event: 1

Committers

Last synced: 10 months ago

All Time
  • Total Commits: 91
  • Total Committers: 1
  • Avg Commits per committer: 91.0
  • Development Distribution Score (DDS): 0.0
Past Year
  • Commits: 0
  • Committers: 0
  • Avg Commits per committer: 0.0
  • Development Distribution Score (DDS): 0.0
Top Committers
Name Email Commits
Andrew McNeil a****l@g****m 91

Issues and Pull Requests

Last synced: 7 months ago

All Time
  • Total issues: 21
  • Total pull requests: 3
  • Average time to close issues: 4 months
  • Average time to close pull requests: 3 months
  • Total issue authors: 6
  • Total pull request authors: 2
  • Average comments per issue: 0.24
  • Average comments per pull request: 0.0
  • Merged pull requests: 3
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 4
  • Pull requests: 1
  • Average time to close issues: 10 days
  • Average time to close pull requests: 9 months
  • Issue authors: 4
  • Pull request authors: 1
  • Average comments per issue: 0.25
  • Average comments per pull request: 0.0
  • Merged pull requests: 1
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • armcn (16)
  • eitsupi (1)
  • atsyplenkov (1)
  • etiennebacher (1)
  • torbjorn (1)
Pull Request Authors
  • jonocarroll (2)
  • armcn (2)
Top Labels
Issue Labels
Pull Request Labels

Packages

  • Total packages: 1
  • Total downloads:
    • cran 506 last-month
  • Total dependent packages: 1
  • Total dependent repositories: 20
  • Total versions: 5
  • Total maintainers: 1
cran.r-project.org: quickcheck

Property Based Testing

  • Versions: 5
  • Dependent Packages: 1
  • Dependent Repositories: 20
  • Downloads: 506 Last month
Rankings
Dependent repos count: 6.3%
Stargazers count: 12.9%
Dependent packages count: 18.2%
Average: 19.9%
Forks count: 27.7%
Downloads: 34.5%
Last synced: 7 months ago

Dependencies

DESCRIPTION cran
  • data.table * imports
  • hedgehog * imports
  • hms * imports
  • magrittr * imports
  • purrr * imports
  • stats * imports
  • testthat >= 3.0.0 imports
  • tibble * imports
  • covr * suggests
  • dplyr * suggests
  • knitr * suggests
  • rmarkdown * suggests
.github/workflows/R-CMD-check.yaml actions
  • actions/checkout v2 composite
  • actions/upload-artifact main composite
  • r-lib/actions/check-r-package v1 composite
  • r-lib/actions/setup-r v1 composite
  • r-lib/actions/setup-r-dependencies v1 composite
.github/workflows/pkgdown.yml actions
  • actions/checkout v2 composite
  • r-lib/actions/setup-pandoc v2 composite
  • r-lib/actions/setup-r v2 composite
  • r-lib/actions/setup-r-dependencies v2 composite
.github/workflows/test-coverage.yaml actions
  • actions/checkout v2 composite
  • r-lib/actions/setup-r v1 composite
  • r-lib/actions/setup-r-dependencies v1 composite