testthat: установить соединение с базой данных, доступное для всех тестов - PullRequest
1 голос
/ 06 июня 2019

Мой пакет R изменяет данные в удаленной БД, и я хотел бы написать несколько тестов с testthat.

Я знаю, что могу издеваться над БД, но я бы просто использовал одну нашу БД разработчика.

Как сделать соединение БД доступным для всех тестов, которые в нем нуждаются, при этом убедившись, что любое созданное соединение разрушено? Кажется очевидным, что подключение должно происходить в setup, а отключение в teardown, но я не справился.

Я пытался поместить следующий код в tests/testthat.R или во вспомогательный файл tests/testthat/helper-_, но безрезультатно.

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")
})

Мои начальные тесты:

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

После первого файла (где проходят все тесты) я получаю Error in DBI::dbDisconnect(db_con) : object 'db_con' not found, который указывает на то, что демонтаж происходит, но db_con не найден.

После этого все тесты, требующие db_con, не пройдут с object 'db_con' not found.

Нужно ли создавать вспомогательный файл для каждого файла, для которого требуется db_con? Или я должен явно указать общий вспомогательный файл?

Можно ли как-нибудь установить соединение где-нибудь и сделать его доступным для всех тестов и уничтожить в конце?

Ответы [ 2 ]

1 голос
/ 06 июня 2019

РЕДАКТИРОВАТЬ : dbDisconnect в test-connect_init. Эта структура лучше всего работает в рабочих процессах, которые получают данные из базы данных (один или несколько раз).

Отказ от ответственности : все, что было успешно протестировано с Impala.

Я выбрал способ поиска, создав функцию connect_init.R, которая вызывается в скрипте и тесте:

Организация

R
├── utils
|   ├── connect_init.R
|   ├── df_import.R
├── clean
|   ├── data_clean.R
tests
├── testthat
│   ├── test-connect.R
│   ├── test-import.R
│   └── test-clean.R
└── testthat.R

Процессы

connect_init.R

connect_init <- function(params) DBI::dbConnect(...)

data_clean.R

[...]
con <- connect_init(params)
rqt <- "select * from db.tab"
dframe <- DBI::dbGetQuery(conn = con, rqt)

# --- when import finished
DBI::dbDisconnect(con)

Тесты

тест-connect.R

context("test-connect")

test_that("connexion to Impala doable",
        res <- mypkg::connect_init(params)
        testthat::expect_true(attributes(res)$class[1] == "Impala")
        DBI::dbDisconnect(res)
})

тест-import.R

context("test-import")

test_that("import from Impala doable", {

        res <- mypkg::df_import(paramsbis)

        testthat::expect_s3_class(object = res, class = "data.frame")
        testthat::expect_true(nrow(res) > 0)
})

Затем откройте и закройте соединение при использовании в других тестах. Я очень заинтересован в другом способе тестирования этого + отзыва о том, как улучшить эту часть.

Должны ли мы хранить минимальные выборочные данные, чтобы тесты, не связанные с подключением, не давали сбой в случае проблем с сетью / БД?

0 голосов
/ 21 июня 2019

Из testthat документов

Код в блоке setup() запускается немедленно в чистой среде

Я считаю, что это означает, что если вы хотите сохранить все объекты, созданные в вашей среде setup, то вам нужно поместить их в глобальную среду

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

Тогда в вашем методе teardown() он сможет найти соединение

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

Это не идеально, чтобы связываться с глобальной средой, но если вы тщательно называете вещи и удаляете их, когда закончите, это не должно быть проблемой.

Кажется, что setup() был разработан больше для чтения / записи временных файлов / темпдиров, чем для создания глобальных объектов, которые будут использоваться всеми тестами, но я могу ошибаться.

Полезный пример в дикой природе, с которым я столкнулся при исследовании этого вопроса: https://github.com/ropensci/Rpolyhedra/blob/3675a3a6eb8b2807f26fb2ebc929b9f5072681db/tests/testthat/test_package_lib.R#L7

...