Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

testthat: set up database connection available to all tests

My R package modifies data in a remote DB and I'd like to write some tests with testthat.

I am aware that I could mock the DB but I'd rather simply use of one our dev DB.

How can I make a db connection available to all tests that need it, while making sure any connection created is destroyed? It seems obvious that connecting should happen in a setup and disconnecting in a teardown, but I haven't managed.

I have tried to put the following code in tests/testthat.R or in a helper file tests/testthat/helper-_, to no avail.

setup({
  # db_connect is just a basic wrapper around RMariaDB::dbConnect with logging
  db_con <- db_connect(conf$database, loglevel = "none")
})

teardown({
  # db_connect is just a basic wrapper around DBI::dbDisconnect with logging
  db_disconnect(db_con = db_con, loglevel = "none")
})

My initial tests are:

tests
├── testthat
│   ├── helper-_.R
│   ├── test-connect.R
│   └── test-questions.R
└── testthat.R

After the first file (where all tests pass), I get Error in DBI::dbDisconnect(db_con) : object 'db_con' not found which indicates the teardown is happening but the db_con is not found.

After that, all tests requiring db_con fail with object 'db_con' not found.

Do I have to create a helper file for each file where db_con is needed? Or do I have to explicitly source a common helper file?

Is there a way I can set up the connection once somewhere and have it available to all tests and destroyed at the end?

like image 578
asachet Avatar asked Jan 26 '26 15:01

asachet


1 Answers

From the testthat docs

Code in a setup() block is run immediately in a clean environment

I believe this means that if you want to save whatever objects are created in your setup environment, then you need to place them in the global environment

setup({
  db_con <- db_connect(conf$database, loglevel = "none")
  assign("db_con", db_con, envir = .GlobalEnv)
})

Then in your teardown() method, it will be able to find the connection

teardown({
  db_disconnect(db_con = db_con, loglevel = "none")
  # Can also remove it from the global environment after disconnect
  rm(db_con, envir = .GlobalEnv)
})

It's not ideal to mess with the global environment, but as long as you name things carefully and remove them when you're done, it shouldn't be a problem.

It seems like setup() was designed more for reading/writing tempfiles/tempdirs than for creating global objects to be used by all tests, but I could be mistaken.

Helpful example in the wild that I came across while researching this question: https://github.com/ropensci/Rpolyhedra/blob/3675a3a6eb8b2807f26fb2ebc929b9f5072681db/tests/testthat/test_package_lib.R#L7

like image 180
Paul Wildenhain Avatar answered Jan 28 '26 05:01

Paul Wildenhain



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!