Setarea valorilor în clasele R6 și testarea cu shiny::MockShinySession

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

(Acest articol a fost publicat pentru prima dată pe Rsarcinași cu amabilitate a contribuit la R-bloggeri). (Puteți raporta problema legată de conținutul acestei pagini aici)


Doriți să vă distribuiți conținutul pe R-bloggeri? dați clic aici dacă aveți un blog, sau aici dacă nu aveți.

Puteți citi postarea originală în formatul său original pe site-ul web Rtask de către ThinkR aici: Setarea valorilor în clasele R6 și testarea cu shiny::MockShinySession

Context

Recent, am lucrat la testarea a {shiny} aplicație care se bazează pe valorile stocate în session$request obiect. Acest obiect este un mediu care captează detaliile schimbului HTTP dintre R și browser. Fără să mă scufund prea adânc în aspecte tehnice (oricât de mult mi-ar plăcea 😅), este important să înțeleg că session$request conține informații furnizate atât de browser, cât și de orice proxy care redirecționează solicitările.

Aplicația noastră este implementată în spatele unui proxy într-un mediu Microsoft Azure. Aici, serviciul de autentificare atașează mai multe anteturi pentru a valida identitatea utilizatorului (consultați documentația pentru detalii). Antete ca X-MS-CLIENT-PRINCIPAL şi X-MS-CLIENT-PRINCIPAL-ID sunt critice pentru identificarea utilizatorilor, iar {shiny} aplicația depinde de acestea pentru a gestiona autentificarea.

Testarea antetelor

Când un utilizator se conectează la aplicație, identificatorii acestuia sunt preluați dintr-un antet și stocați pentru utilizare în întreaga aplicație. Iată un exemplu simplificat despre cum ar putea funcționa:

library(shiny)

ui <- fluidPage(
  textOutput("user_id")
)

server <- function(input, output, session) {
  r <- reactiveValues(
    email = NULL
  )

  observe({
    r$email <- session$request$HTTP_X_MS_CLIENT_PRINCIPAL_NAME
  })

  output$user_id <- renderText({
    req(r$email)
    sprintf("Hello %s", r$email)
  })
}

shinyApp(ui, server)

Testarea acestei funcționalități, în special în mediile de integrare continuă (CI), poate fi o provocare.

În cazul nostru de utilizare, ne-ar plăcea să avem ceva de genul acesta:

test_that("app server", {

  # Tweaking the session here

  testServer(app_server, {
    # Waiting for the session to be fired up
    session$elapse(1)

    expect_equal(
      r$email,
      "(email protected)"
    )
  })
})

Dar anteturile de autentificare ca HTTP_X_MS_CLIENT_PRINCIPAL_NAME sunt absente în timpul testelor automate, așa că avem nevoie de o modalitate de a simula prezența lor. {shiny} oferă MockShinySession clasă pentru testare, dar nu simulează în mod nativ o clasă realistă session$request obiect. Să explorăm cum să rezolvăm această limitare.

Depășirea session$request

Mai întâi încercăm să modificăm direct session$requestdar nu merge:

> session <- MockShinySession$new()
> session$request

Warning message:
In (function (value)  :
  session$request doesn't currently simulate a realistic request on MockShinySession

Ok, poate putem atribui o nouă intrare aici?

> session$request$HTTP_X_MS_CLIENT_PRINCIPAL_NAME <- "test"
Error in (function (value)  : session$request can't be assigned to
In addition: Warning message:
In (function (value)  :
  session$request doesn't currently simulate a realistic request on MockShinySession

Aa, nu funcționează, nu poate fi atribuit. Dar să continuăm explorarea noastră. Ce este session?

> class(session)
(1) "MockShinySession" "R6"
> class(session$request)
(1) "environment"

După cum putem vedea, este un obiect R6, o instanță a MockShinySession clasa, si session$request un env. Ceea ce ne dorim este să putem accesa, în aplicația noastră, la session$request$HTTP_X_MS_CLIENT_PRINCIPAL_NAME. Poate am putea trece peste request?

request este cuprinsă în active câmp din clasa R6:

> MockShinySession$active
# (...)

$request
function (value)
{
    if (!missing(value)) {
        stop("session$request can't be assigned to")
    }
    warning("session$request doesn't currently simulate a realistic request on MockShinySession")
    new.env(parent = emptyenv())
}


To override the request object, we can use the set() method of the R6 class. Here’s how we redefine the behavior:

MockShinySession$set(
    "active",
    "request",
    function(value) {
      return(
        list(
          "HTTP_X_MS_CLIENT_PRINCIPAL_NAME" = "(email protected)"
        )
      )
    },
    overwrite = TRUE
  )

Acum, sesiunea se comportă conform așteptărilor:

> session <- MockShinySession$new()
> session$request
$HTTP_X_MS_CLIENT_PRINCIPAL_NAME
(1) "(email protected)

Scrierea Testului

Cu anulat requestacum putem scrie un test funcțional:

test_that("app server", {
  MockShinySession$set(
    "active",
    "request",
    function(value) {
      return(
        list(
          "HTTP_X_MS_CLIENT_PRINCIPAL_NAME" = "(email protected)"
        )
      )
    },
    overwrite = TRUE
  )

  testServer(app_server, {
    # Waiting for the session to be fired up
    session$elapse(1)

    expect_equal(
      r$email,
      "(email protected)"
    )
  })
})

Curățarea după teste

Dar, încă un lucru: trebuie să curățăm testul, astfel încât obiectul sesiune să rămână același după test. Pentru aceasta, vom folosi on.exit pentru a restabili vechiul comportament:

test_that("app server", {
  old_request <- MockShinySession$active$request
  on.exit({
    MockShinySession$set(
      "active",
      "request",
      old_request,
      overwrite = TRUE
    )
  })
  MockShinySession$set(
    "active",
    "request",
    function(value) {
      return(
        list(
          "HTTP_X_MS_CLIENT_PRINCIPAL_NAME" = "(email protected)"
        )
      )
    },
    overwrite = TRUE
  )

  testServer(app_server, {
    # Waiting for the session to be fired up
    session$elapse(1)

    expect_equal(
      r$email,
      "(email protected)"
    )
  })
})

Această configurare asigură că testele noastre rămân izolate și fiabile, chiar și în medii CI. Prin valorificarea flexibilității lui R6, putem controla pe deplin și ne batem joc session$request pentru a testa logica dependentă de autentificare.

Dacă doriți să explorați mai multe detalii, puteți vizita acest depozit, unde veți găsi un exemplu reproductibil!

Aveți nevoie de ajutor pentru a vă testa aplicațiile?

Încă nu sunteți sigur cum să implementați o strategie bună de testare pentru aplicația dvs.? Hai să discutăm!

Această postare este mai bine prezentată pe site-ul său original ThinkR aici: Setarea valorilor în clasele R6 și testarea cu shiny::MockShinySession


Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.