3

We're building an R codebase and are hoping to unittest any functions that we write. So far, we have found two testing libraries for R: RUnit and testthat.

After doing a bit of sandboxing, we have developed a solid method for testing code every time it runs. E.g.:

# sample.R

library(methods) #required for testthat
library(testthat)

print("Running fun()...")
fun <- function(num1, num2){
    num3 = num1 + num2
    return(num3)
}

expect_that(fun(1,2), equals(3))

Simple enough. However, we would also like the ability to test the function (with a unittest flag in a makefile for example) without running the script it is defined in. To do this we would write unittests in test.R

# test.R

source("sample.R")

expect_that(fun(2,3), equals(5))

and run it without running the rest of sample.R. But, when the code above is run, not only the functions but the rest of the code from sample.R will be run, in this example outputting "Running fun()...". Is there any way to source() only the user-defined functions from a file?

If not, would you recommend putting functions in a separate file (say, functions.R) which can be unittested by being sourced into test.R and run when sourced in sample.R? The drawback there, it would seem, is the boilerplate needed: a file for the process, a file for the functions, and a file to run the tests.

stephentgrammer
  • 470
  • 6
  • 16
  • 3
    You don't say so explicitly, but I gather that you aren't putting these functions in a package. The best way forward is probably going to switch at least part of your code away from standalone scripts and into packages. – joran Sep 08 '14 at 19:36

2 Answers2

2
  1. In each script, set a name variable that is defined only if it is not already defined. See exists(). I'm fond of __name__.

  2. Create a main function in each script that only runs if the name is correct. This function contains everything you only want to run if this is the top level script.

This is similar to the python structure of

if __name__ == "__main__": main()
Will Beason
  • 3,417
  • 2
  • 28
  • 46
1

I didn't understand how to implement Will Beason's answer, but his mention of the pythonic way lead me to find this solution:

if (sys.nframe() == 0) {
    # ... do main stuff
}

sys.nframe() is equal to 0 when run from the interactive terminal or using Rscript.exe, in which case the main code will run. Otherwise when the code is sourced, sys.nframe() is equal to 4 (in my case, not sure how it works exactly) which will prevent the main code from running.

source

Bakr
  • 117
  • 8