Cum se scrie teste robuste ShinyTest2 pentru aplicații strălucitoare

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

(Acest articol a fost publicat pentru prima dată pe Jakub :: Sobolewskiși a contribuit cu drag la R-Bloggers). (Puteți raporta problema despre conținutul de pe această pagină aici)


Doriți să vă împărtășiți conținutul pe R-Bloggers? Faceți clic aici dacă aveți un blog sau aici dacă nu.

Majoritatea testelor ShinyTest2 se rup prea ușor.

Acestea expun detalii fragile ale aplicației dvs. care vă rup testele pe măsură ce implementarea dvs. se modifică. Este fie prin a face instantanee, fie prin utilizarea ID -urilor de intrare brute. Dar puteți prelua controlul înapoi: faceți teste descrieți comportamentnu cablare.

Problema cu ShinyTest RAW2

ShinyTest2 este minunat.

Într -adevăr democratizează testarea pentru aplicații strălucitoare. Este ușor de instalat. Este ușor să îți faci testele strălucitoare.

Dar, din păcate, vă cuplă testele strâns cu interioare de strălucire și vă atrage să folosiți instantanee fragile. Un implicit, foarte minim shinytest2 Testul va arăta astfel (și probabil acest lucru cât de simplu puteți obține):

test_that("...", {
  driver <- shinytest2::AppDriver$new()
  driver$click("setup-data-next")
  driver$click("setup-data-next")
  driver$click("setup-data-next")
  driver$click("setup-data-next")
  driver$set_inputs("plots-colorby" = "AGE")
  # Verify outcome
})

Funcționează, dar scurge internele aplicației. ID -urile de intrare se schimbă atunci când refactorul, reproiectați sau evoluați doar aplicația. Setarea intrărilor prin ID -uri RAW își leagă testele de „cum”, nu de „ce”.

Testul dvs. nu poate eșua nu pentru că comportamentul s -a schimbat, ci pentru că selectorii dvs. au făcut -o.

Pasul 1. Utilizare data-testid în loc de ID -uri de intrare

htmltools :: tagapendatTributes vă permite să decorați widget -urile cu atribute. Unul dintre cele mai bune cadouri pe care le puteți da teste: adăugați data-testid (sau utilizați un nume de atribut diferit, dar folosiți -l doar pentru testare).

  • id a unei intrări sau a unei ieșiri se modifică atunci când evoluează codul tău strălucitor.
  • data-testid se schimbă numai când sens de afaceri modificări.

Acest lucru menține testele aliniate cu logica, nu cu structura codului.

Refactor componentele dvs. pentru a atașa data-testid

picker_input <- function(inputId, ..., testid) {
  shinyWidgets::pickerInput(
    inputId,
    ...
  ) |>
    htmltools::tagAppendAttributes(
      `data-testid` = testid,
      .cssSelector = "select"
    )
}

Asigurați -vă că vă atașați data-testid la eticheta HTML cu ID de intrare. În caz de shinyWidgets::pickerInput este select etichetă. Apoi în codul aplicației, înlocuiți pickerInput(...) cu de exemplu picker_input(..., testid = "plot_color_variable").

Este o refactorizare foarte sigură, nu vă faceți griji.

Apoi, în loc să folosim ID -uri de intrare în codul de testare, putem folosi data-testid Pentru a obține ID -ul de intrare:

driver <- shinytest2::AppDriver$new()
id <- driver$get_js(
  sprintf("$('(data-testid=%s)').attr('id')", testable_id)
)
driver$set_inputs(!!!rlang::list2(!!id := "AGE"))

Acum pare destul de complex, dar se va îmbunătăți.

Utilizare data-testtype pentru expediere mai inteligentă

Un alt refactorizare simplă care ne va facilita viața de testare este adăugarea data-testtype.

De ce faci asta?

  • data-testid ne spune care este componenta cu care vrem să interacționăm,
  • data-testtype ne spune cum să interacționăm cu componenta.

Să creăm un unic data-testtype pentru fiecare componentă din biblioteca noastră:

picker_input <- function(inputId, ..., testid) {
  shinyWidgets::pickerInput(
    inputId,
    ...
  ) |>
    htmltools::tagAppendAttributes(
      `data-testid` = testid,
      `data-testtype` = "picker_input",
      .cssSelector = "select"
    )
}

Cea mai ușoară alegere este să folosim doar numele componentei pe care o folosim. Va fi ușor de corelat cu codul de testare.

Pasul 3. Cod de testare a refactorului

Acum în loc să folosească AppDriver În mod direct, îl extindem cu propria noastră clasă de șofer care folosește data-testid şi data-testtype pentru localizarea componentelor.

action <- function(type, id, ..., driver) {
  switch(type,
    action_button = driver$click(id),
    picker_input = driver$set_inputs(
      !!!rlang::list2(!!id := rlang::list2(...)((1)))
    )
  )
}

ShinyDriver <- R6::R6Class(
  inherit = shinytest2::AppDriver,
  public = list(
    dispatch = function(testable_id = missing_arg(), ...) {
      id <- self$get_js(
        sprintf("$('(data-testid=%s)').attr('id')", testable_id)
      )
      type <- self$get_js(
        sprintf("$('(data-testid=%s)').attr('data-testtype')", testable_id)
      )
      action(type, id, ..., driver = self)
    }
  )
)

Atunci testul nostru devine:

test_that("...", {
  # Given
  driver <- ShinyDriver$new()
  driver$dispatch("next")
  driver$dispatch("next")
  driver$dispatch("next")
  driver$dispatch("next")

  # When
  driver$dispatch("plot_color_variable", c("AGE"))

  # Then
  # Verify the outcome

  # Teardown
  driver$stop()
})

Pasul 4. Înveliți acțiuni reutilizabile în funcții de vitreg

Testele sunt pentru ca oamenii să citească mai întâi, mașinile în al doilea rând. Înfășurarea interacțiunilor comune în funcții oferă limbajul tău de testare:

i_use_default_mapping <- function(driver) {
  driver$dispatch("next")
  driver$dispatch("next")
  driver$dispatch("next")
  driver$dispatch("next")
}

i_set <- function(what, to, driver) {
  driver$dispatch(what, to)
}

Acum testul tău arată astfel:

test_that("...", {
  # Given
  driver <- ShinyDriver$new()
  i_use_default_mapping(driver)

  # When
  i_set("plot_color_variable", to = "AGE", driver)

  # Then
  # Verify the outcome

  # Teardown
  driver$stop()
})

O citiți ca: Având în vedere că folosesc maparea implicită, când am setat variabila de culoare a complotului la vârstă, atunci ar trebui să văd complotul.

Asta este Specificații executabile.

Pasul 5. Utilizați castraveți (opțional)

De acolo puteți vedea că aproape că am obținut sintaxa de castraveți cu cod.

Pentru a facilita colaborarea dintre dezvoltatori și non-dezvoltatori, puteți utiliza castraveți pentru a vă scrie testele într-un format de limbaj și mai natural. Acest lucru permite părților interesate să înțeleagă și să contribuie mai ușor la procesul de testare.

Atunci acest caz de testare va deveni:

Given I use default mapping
When I set "plot_color_variable" to "AGE"
# Then verify the outcome

Cu această abordare ne organizăm codul de testare către:

  • Lizibilitate: Testele sunt mai ușor de citit și de înțeles.
  • Reutilizare: Acțiunile comune sunt încapsulate în funcții.
  • Mentenabilitate: Modificările în interfața de utilizare a aplicației necesită minim (sau nu!) modificări ale testelor.

Această abordare păstrează sensul testelor de afaceri, permite testelor să evolueze alături de baza dvs. de cod, permițându -vă să vă mențineți viteza de dezvoltare ridicată.


Nu lăsați testele dvs. să alunge detaliile implementării.

Echipați -le cu cârlige stabile, lăsați șoferul să trimită acțiuni în mod inteligent și să exprime comportamentul cu pași reutilizabili. Cu acești patru pași, shinytest2 Suite vorbește aceeași limbă ca și utilizatorii dvs .: acțiuni, nu cablare și facilitează întreținerea testelor.

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.