Skip to content

5. Testing & Test Creation

Gram edited this page Jul 22, 2025 · 15 revisions

Our implementation

We implement function testing using the testthat package, which is automatically called via devtools, but can also be called on their own to run specific tests (more on this below!). We set up tests so that they automatically run whenever devtools::check() is ran. The purpose of a test is to test the functionality of a function. We do this by running the function over example data and checking to make sure the output is correct. This way, if we make a change that unintentionally changes how a function works, we will know because the test will fail.

Running tests

Testthat is already completely set up in the PTMsToPathways package, so you just need to know how to use it, which is very easy. To run a test, you can use this command in an R console (You can omit "testthat::" if you install testthat package):

testthat::test_file("tests/testthat/test-functionname.R")

Note that the test_file() function requires a path to the file you want to test. The above assumes your working directory is in the highest level of the PTMsToPathways directory. Thus, it may be different depending on where you perfer to keep your working directory. If you are using Rstudio, this process is much simpler. Just open the file you want to test and click the "Run Tests" button in the top right corner of the window: 2025-07-21_14-20

Writing tests

To create a test file, keep in mind these two requirements: But, before you can run tests, there are two prerequisites that must be met.

  1. A test file must be an R script in the tests/testthat directory.
  2. It must start with "test-". Also, it is highly recommended that the name of the function being tested is included in the name (I.e. "test-function.R"). For the sake of consistency and clarity.

In order to create tests, you need to be familiar with two functions: testthat and expect_equal. The expect_equal function is the more important of the two, as it contains the actual test for equality given data and an expected value. It also possess a tolerance parameter which, when set, means the data and expected value do not need to be exactly equal, instead it can be off by as much as the tolerance allows. Tolerance is extremely useful since our code mainly works with doubles (floats) that need to be rounded (We generally use tolerance=0.00001) in order to create concise test statements. The testthat function is mainly for documentation as it provides an accurate description of the test.

test_that("name", {expect_equal( data[3], value, tolerance=0.00001 )} )


Problems and Solutions

Tests should be both subtle, meaning the user shouldn't have to see any output from them as well as consistent. To help maintain this policy, here are some solutions to common problems we encountered.

  1. Tests should NEVER require input on the user's end, with the exception of telling a computer to actually run the test. Every parameter for the function being tested should be called within the test. You can use example data in order to run functions such as:

PathwayCrosstalkNetwork(ex.common.clusters, ex.bioplanet, edgelist.name = "edgelist_123a", createfile = FALSE)

  1. Functions like MakeClusterList() which make use of randomization. We can't reliably say a random number 1 through 6 = 3 and we need tests to return the same result every time. To solve this, we can call set.seed(1) at the before MakeClusterList runs to make the result consistent every time the test file runs.
  2. Our functions are fairly unique, as their output is placed in the global environment. This means that if a test were to output a variable that is the same name as a variable in the global environment, it would get overwritten with the test's variable. To avoid this, we pass in names that are unlikely to be used by another user into our functions. Additionally, you can call rm(sillyname) afterwards to avoid cluttering the global environment with test variables. You should remove EVERYTHING a test creates so they are unobtrusive for the user.
  3. In order to silence print statements that you may encounter, the sink function creates a file that redirects all print statement to itself instead of the console. You can find a working example of this in tests-PathwayCrosstalkNetwork.R. Just be sure to remove the file afterwards using file.remove.

sink(file="noprint_123a")
yourfunction()
sink()
file.remove("noprint_123a")

  1. Note that you can test booleans as well with by replacing value with TRUE or FALSE. This is especially helpful with is.na(data), as many data types in R have their own form of the NA operator which is used in our data structures, which will produce false when compared with value if it's NA, as R considers that a logical operator.

Clone this wiki locally