测试模块化 R Shiny (golem) 仪表板
Testing modular R Shiny (golem) dashboards
我一直在探索(**并喜爱)golem 包,用于使用 R Shiny 开发模块化仪表板。但我正在努力思考如何测试模块化仪表板。
例如,在下面的 repex 中,如果导入模块中的输入 $n_rows 设置为 15,那么显示模块中的输出包含 15 行,我将如何测试?
我将非常感谢对此的任何支持!
library(shiny)
library(reactable)
library(dplyr)
# Import module UI
mod_import_ui <- function(id){
ns <- NS(id)
fluidRow(
# Allow the user to select the number of rows to view
numericInput(ns("n_rows"),
"Select number of observations",
value = 10)
)
}
# Import module Server
mod_import_server <- function(id){
moduleServer(
id,
function(input, output, session){
data <- reactive({
# Sample the requested number of rows from mtcars and return this to the application server
mtcars %>%
slice_sample(n = input$n_rows)
# [....] # Some complex formatting and transformations
})
return(data)
}
)}
# Display module UI
mod_display_ui <- function(id){
ns <- NS(id)
fluidRow(
reactableOutput(ns("table"))
)
}
# Display module Server
mod_display_server <- function(id, data_in){
moduleServer(
id,
function(input, output, session){
# [....] # Some more transformations and merging with data from other modules
output$table <- renderReactable(reactable(data_in()))
}
)}
app_ui <- function(request) {
tagList(
mod_import_ui("import_1"),
mod_display_ui("display_1")
)
}
app_server <- function(input, output, session) {
data_in <- mod_import_server("import_1")
mod_display_server("display_1", data_in)
}
shinyApp(ui = app_ui, server = app_server)
我建议将应用程序的核心与用户界面分开。
{golem} 框架允许在 R 包中构建您的应用程序,这意味着您可以使用包构建中的所有工具来记录和测试您的代码。
如果您遵循我们在 engineering-shiny.org/ 中的指南,您会发现我们建议从您的“服务器”部分提取所有 R 代码以在 vignette 中对其进行测试,将其转换为常规函数,以便您可以照常进行测试使用 R 包。
因此,您 ShinyApp 只调用已经记录和测试的内部函数。使用这种方法,您可以测试应用程序中可能发生的不同场景的输出。在静态脚本中尝试不同的输入参数并验证输出,无论您在接下来的开发步骤中对您的应用程序进行什么更改。
这本书给了很多建议。如果我必须将它们总结为一个工作流程,这将是:
- 直接在 Rmd 中构建必要的代码。这使您无需进行所有必要的点击即可测试操作。我们称之为“Rmd first”方法:https://rtask.thinkr.fr/when-development-starts-with-documentation/
- 将此代码分解为 R 函数,以尽可能少地放入 Shiny 应用程序本身。
- 在没有服务器的情况下创建您的 UI 部分(或不要太多),只是为了看看总体外观如何
- 在应用程序的适当位置包含您的功能。
- 加强代码。可重现的示例、单元测试、文档、代码版本控制……(此步骤与代码并行完成时效果更好)
作为对 Sebastien 回答的补充,我想指出从 {shiny}
v 1.5.0 开始,您可以直接使用 testServer
函数测试服务器功能,这可能是您想要的正在寻找。
以下是如何实现该目标的代表:
library(shiny)
library(magrittr)
library(dplyr)
mod_import_server <- function(id){
moduleServer( id, function(input, output, session){
data <- reactive({
mtcars %>%
slice(n = 1:input$n_rows)
})
return(data)
})
}
shiny::testServer(mod_import_server, {
for (i in 1:10){
session$setInputs(n_rows = i)
testthat::expect_equal(
data(),
slice(mtcars, n = 1:i)
)
}
})
在这里,您可以测试您的 reactive()
行为是否符合您的预期。
这并不完美,但却是一个好的开始:)
但是很难找到一种方法来测试第二个模块的行为,因为它依赖于作为值传递的 reactive()。
科林
我一直在探索(**并喜爱)golem 包,用于使用 R Shiny 开发模块化仪表板。但我正在努力思考如何测试模块化仪表板。
例如,在下面的 repex 中,如果导入模块中的输入 $n_rows 设置为 15,那么显示模块中的输出包含 15 行,我将如何测试?
我将非常感谢对此的任何支持!
library(shiny)
library(reactable)
library(dplyr)
# Import module UI
mod_import_ui <- function(id){
ns <- NS(id)
fluidRow(
# Allow the user to select the number of rows to view
numericInput(ns("n_rows"),
"Select number of observations",
value = 10)
)
}
# Import module Server
mod_import_server <- function(id){
moduleServer(
id,
function(input, output, session){
data <- reactive({
# Sample the requested number of rows from mtcars and return this to the application server
mtcars %>%
slice_sample(n = input$n_rows)
# [....] # Some complex formatting and transformations
})
return(data)
}
)}
# Display module UI
mod_display_ui <- function(id){
ns <- NS(id)
fluidRow(
reactableOutput(ns("table"))
)
}
# Display module Server
mod_display_server <- function(id, data_in){
moduleServer(
id,
function(input, output, session){
# [....] # Some more transformations and merging with data from other modules
output$table <- renderReactable(reactable(data_in()))
}
)}
app_ui <- function(request) {
tagList(
mod_import_ui("import_1"),
mod_display_ui("display_1")
)
}
app_server <- function(input, output, session) {
data_in <- mod_import_server("import_1")
mod_display_server("display_1", data_in)
}
shinyApp(ui = app_ui, server = app_server)
我建议将应用程序的核心与用户界面分开。
{golem} 框架允许在 R 包中构建您的应用程序,这意味着您可以使用包构建中的所有工具来记录和测试您的代码。
如果您遵循我们在 engineering-shiny.org/ 中的指南,您会发现我们建议从您的“服务器”部分提取所有 R 代码以在 vignette 中对其进行测试,将其转换为常规函数,以便您可以照常进行测试使用 R 包。
因此,您 ShinyApp 只调用已经记录和测试的内部函数。使用这种方法,您可以测试应用程序中可能发生的不同场景的输出。在静态脚本中尝试不同的输入参数并验证输出,无论您在接下来的开发步骤中对您的应用程序进行什么更改。
这本书给了很多建议。如果我必须将它们总结为一个工作流程,这将是:
- 直接在 Rmd 中构建必要的代码。这使您无需进行所有必要的点击即可测试操作。我们称之为“Rmd first”方法:https://rtask.thinkr.fr/when-development-starts-with-documentation/
- 将此代码分解为 R 函数,以尽可能少地放入 Shiny 应用程序本身。
- 在没有服务器的情况下创建您的 UI 部分(或不要太多),只是为了看看总体外观如何
- 在应用程序的适当位置包含您的功能。
- 加强代码。可重现的示例、单元测试、文档、代码版本控制……(此步骤与代码并行完成时效果更好)
作为对 Sebastien 回答的补充,我想指出从 {shiny}
v 1.5.0 开始,您可以直接使用 testServer
函数测试服务器功能,这可能是您想要的正在寻找。
以下是如何实现该目标的代表:
library(shiny)
library(magrittr)
library(dplyr)
mod_import_server <- function(id){
moduleServer( id, function(input, output, session){
data <- reactive({
mtcars %>%
slice(n = 1:input$n_rows)
})
return(data)
})
}
shiny::testServer(mod_import_server, {
for (i in 1:10){
session$setInputs(n_rows = i)
testthat::expect_equal(
data(),
slice(mtcars, n = 1:i)
)
}
})
在这里,您可以测试您的 reactive()
行为是否符合您的预期。
这并不完美,但却是一个好的开始:)
但是很难找到一种方法来测试第二个模块的行为,因为它依赖于作为值传递的 reactive()。
科林