February 22, 2017

genthat

An R package for automated unit test generation for R packages.

Motivation

my_function <- function(x,y,z) { ... }
  • there are no unit tests
  • but there is some code using this function
  • genthat can turn this code into unit tests.

Example

In primes package:

is_prime <- function(n) {
  if (n == 2L) TRUE
  else if (all(n %% 2L:max(2, floor(sqrt(n))) != 0)) TRUE
  else FALSE
}

Example

In primes package:

is_prime <- function(n) {
  if (n == 2L) TRUE
  else if (all(n %% 2L:max(2, floor(sqrt(n))) != 0)) TRUE
  else FALSE
}

program.R:

... ... ...
if (is_prime(val)) { 
  ... ... ...
}
... ... ...

Example

library(genthat)
gen_from_source("program.R", package="primes", verbose=TRUE)

Example

library(genthat)
gen_from_source("program.R", package="primes", verbose=TRUE)
... ... ...
All functions from package will be decorated
Tracing function "is_prime" in package "primes"
Running program.R
... ... ...

Example

library(genthat)
gen_from_source("program.R", package="primes", verbose=TRUE)
... ... ...
All functions from package will be decorated
Tracing function "is_prime" in package "primes"
Running program.R
... ... ...

genthat/primes/test-isprime.R:

library(testthat)

test_that("1", expect_equal(primes:::is_prime(n=1459), TRUE))

given that val was 1459.

Running genthat

  1. Need to identify which functions to trace
    • e.g. package=... captures all functions from a package
  2. Need to provide code to run
    • gen_from_function: runs given function
    • gen_from_source: runs given file(s)
    • gen_from_package: runs package's examples, vignettes and tests
    • gen_from_code: runs any code
gen_from_source("program.R", package="primes", verbose=TRUE)

Pruning tests

gen_from_code(code={ Filter(is_prime, 2:10) }, 
              package="primes", 
              verbose=TRUE)

genthat/primes/test-isprime.R:

test_that("1", expect_equal(primes:::is_prime(n=2), TRUE))
test_that("2", expect_equal(primes:::is_prime(n=3), TRUE))
test_that("3", expect_equal(primes:::is_prime(n=4), FALSE))
test_that("4", expect_equal(primes:::is_prime(n=5), TRUE))
... ... ...
test_that("9", expect_equal(primes:::is_prime(n=10), FALSE))

Pruning tests

gen_from_code(code={ Filter(is_prime, 2:10) }, 
              package="primes", 
              verbose=TRUE,
              prune=TRUE)
... ... ...
Pruning tests - this may take some time...
Number of test cases -  5
Test case  tests/testthat/test-isprime.R:1  increased the coverage
Test case  tests/testthat/test-isprime.R:2  increased the coverage
Test case  tests/testthat/test-isprime.R:3  increased the coverage
Test case  tests/testthat/test-isprime.R:4  didn't increase coverage
Test case  tests/testthat/test-isprime.R:5  didn't increase coverage
... ... ...

Summary

$ git shortlog -sn | cut -f 2
Petr Maj
Filippo Ghibellini
Roman Tsegelskyi
Lei Zhao
Filip Krikava