(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).
ida unei intrări sau a unei ieșiri se modifică atunci când evoluează codul tău strălucitor.data-testidse 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-testidne spune care este componenta cu care vrem să interacționăm,data-testtypene 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.
