Как мы можем тестировать функции, которые не отображаются при создании пакетов R?
В настоящее время я разрабатываю графический пакет анализа для R. Мы пытаемся использовать принципы как из Очистить код и Test-Driven Development (TDD). Но мы столкнулись с концептуальной проблемой.
Как вы можете протестировать неэкспонированные функции?
Рассмотрим следующий упрощенный пример. Outer()
- это функция, которую мы создаем в нашем пакете, и мы предоставляем ее пользователям, указав ее в NAMESPACE
. Inner()
- короткая (~ 5-строчная) служебная функция:
Outer <- function(...) {
Inner <- function(...) {
return(x)
}
return( Inner() )
}
Большинство пользовательских функций в нашем пакете являются коллекциями коротких однополых функций стиля Inner()
, которые лежат под зонтиком одной функции Outer()
, которую пользователь может вызвать. Granova.ds.ggplot()
- один из примеров. Такой модульный дизайн сильно защищен Clean Code, и мы вполне довольны результатами. Но мы не знаем, как создавать для него модульные тесты, поскольку функции, которые мы хотим протестировать, недоступны вне области Granova.ds.ggplot()
, как мы ее разработали.
Нам пришло несколько идей/решений:
- Тесты должны иметь доступ только к общедоступным API. Поскольку функции типа
Inner()
по дизайну не являются общедоступными (они не экспортируются в NAMESPACE
), мы даже не пытаемся их протестировать.
- Hadley Wickham
testthat
package wiki говорит, что поддерживает тестирование "неэкспортированных функций" с использованием рабочего процесса R CMD check
.
- Если все остальное не удается, мы могли бы как-то вручную разбить наши функции
Outer()
для целей тестирования
Ни одно из этих решений не работает до сих пор
Решение 1 похоже на копирование. Мы считаем, что особенно в R, разумно иметь функцию верхнего уровня, которая вызывает различные короткие, однопользовательские методы полезности. Невозможность протестировать такие методы кажется глупым, и вроде того, что там есть решение, но мы еще не нашли его.
Решение 2 может работать, но до сих пор мы не работали. Если вы попытаетесь клонировать наш репозиторий, то sourcing inst/dev.R
Я считаю, что вы найдете в тестовом выходе, что он не может найти функцию InitializeGgplot()
, хотя указанная функция определена в строках 613-615 granova.1w.ggplot()
. Точно так же запуск R CMD check
на терминале вообще не выполняется ни одним из наших тестов. Это занимает много времени и набрасывает на нас оскорбительные ошибки: - (
Решение 3 в некотором смысле прагматично, но противоречит цели всегда двигаться к цели цели проекта. Это не имеет смысла для нас.
Каким будет идеальное решение
В идеале мы стремимся использовать такой пакет, как testthat
, чтобы быстро предоставлять обратную связь по мере кода и позволять нам тестировать такие функции, как Inner()
, которые существуют в таких функциях, как Outer()
. Еще лучше было бы сделать это, не прибегая к R CMD check
, который из-за 3-й сложности некоторых наших функций занимает почти полную минуту, чтобы запускать каждый раз.
Итак, что нам не хватает? Должны ли методы TDD разрешать тестирование настроек внешнего/внутреннего стиля в R? Если да, то как мы можем это сделать при разработке нашего пакета? Любая обратная связь приветствуется, и я постараюсь ответить на все, что неясно.
Спасибо!
Ответы
Ответ 1
Если Inner
реализует нетривиальные функции, которые вы хотите протестировать, я бы предложил переместить Inner
на верхний уровень, но не экспортировать его. Как правило, я избегаю функции вложенности в других функциях именно по этой причине - их трудно проверить.
Вы можете тестировать во время разработки с помощью обычных функций testthat, потому что вы, вероятно, просто находите источники во всем своем R-коде и не беспокоитесь о пространствах имен (по крайней мере, так, как я развиваюсь). Затем вы используете R CMD check
в сочетании с test_package
, чтобы убедиться, что тесты все еще работают во время сборки. test_packages
запускает тесты в пространстве имен пакетов, чтобы они могли тестировать неэкспортируемые функции.
Ответ 2
IMO здесь нет проблем - Inner - это просто неотъемлемая часть Outer, поэтому тестирование внешних тестов Inner. Готовы ли вы тестировать анонимные функции? То же самое здесь.