Simplificarea interacțiunilor cu widget-uri complexe în shinytest2 folosind API-uri JavaScript

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

(Acest articol a fost publicat pentru prima dată pe jakub::sobolewskiș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.

shinytest2 este puternic pentru scrierea eficientă a testelor Shiny, dar AppDrivermetodele lui nu acoperă întotdeauna fiecare interacțiune de care aveți nevoie.

S-ar putea să descoperi că este convenabil set_inputs() metoda nu funcționează pentru toate. Ia shiny::selectizeInput cu opțiunea de creare activată. în timp ce set_inputs() va funcționa pentru selectarea opțiunilor existente, se împiedică atunci când încercați să adăugați altele noi.

În loc să te lupți cu abstracții de testare la nivel înalt sau să încerci să simuleze clicuri individuale pentru a interacționa corect cu widget-ul, soluția constă în înțelegerea modului în care widget-urile tale funcționează de fapt sub capotă.

Și acolo API-urile JavaScript devin arma ta secretă.

Crește-ți jocul de testare! Luați o copie a foii de parcurs de testare R.

Provocarea apare deoarece widget-urile complexe, cum ar fi selectarea intrărilor, operează prin propriul lor strat JavaScript.

Când shinytest2 nu poate interacționa direct cu ei, ați putea fi tentat să simulați ce ar face un utilizator: deschideți un meniu derulant, introduceți o opțiune, confirmați selecția, închideți meniul derulant. Această coregrafie manuală nu este doar plictisitoare, ci și fragilă.

Sau ați putea fi tentat să săriți peste testele de scriere pentru aceste interacțiuni, ceea ce este și mai rău.

Dar dacă decideți să implementați acele interacțiuni pas cu pas, fiecare pas introduce potențiale puncte de eșec:

  • Apar probleme de sincronizare: s-a terminat această animație?
  • Managementul statului devine imprevizibil: este deja deschis acest meniu derulant?

O modificare a modului în care se redă widgetul și întregul test se întrerupe.

Simplificarea interacțiunilor cu API-urile JavaScript

În loc să orchestrați o serie de acțiuni UI, puteți apela direct API-ul JavaScript al widget-ului.

Pentru crearea de articole noi în shiny::selectizeInputaceasta înseamnă ocolirea completă a coregrafiei UI și utilizarea createItem metodă:

app <- shinytest2::AppDriver$new(...)

# Use namespaced inputId
app$run_js(sprintf(
  "$('#%s select')(0).selectize.createItem('%s');",
  inputId,
  value
))

# If you're using test selectors - I highly recommend it
app$run_js(sprintf(
  "$('(data-testid=%s) select')(0).selectize.createItem('%s');",
  testid,
  value
))

Această abordare realizează în un apel API ceea ce altfel ar necesita mai multe acţiuni secvenţiale. Nu trebuie să așteptați ca meniurile derulante să se anime. Fără întârzieri la tastare. Fără pași de confirmare. Doar manipulare directă, instantanee a stării widget-ului.

Interacțiunea manuală ar arăta cam așa

Acesta este pseudocod, pentru a ilustra complexitatea:

# Find HTML tag to click to trigger opening dropdown
# Find  to type new option
# Find HTML tag to click to confirm the new option
# Use HTML tag to close the dropdown

Cu abordarea API, eliminați complexitatea orchestrației. Codul devine mai scurt, mai clar și mai puțin dependent de detaliile de implementare care s-ar putea modifica.

Depindem de propriul API al widget-ului pentru a gestiona corect starea internă, așa că suntem încă expuși modificărilor în implementarea widget-ului, dar:

  • Robustitatea se îmbunătățește dramatic deoarece nu vă bazați pe sincronizarea interfeței de utilizare, cadrele de animație sau secvențierea evenimentelor. API-ul widget-ului este un contract stabil. Când suni createItem()funcționează la fel de fiecare dată, indiferent de starea UI din jur. Dacă API-ul widget-ului se modifică, trebuie doar să actualizați acel singur apel API în teste, nu o serie întreagă de interacțiuni UI.
  • Claritatea crește deoarece codul dvs. de testare exprimă direct intenția. În loc de o secvență lungă de manipulări ale interfeței de utilizare, aveți o singură linie care spune clar „creați acest articol”. Acest lucru face testele mai ușor de citit și de întreținut.

Dar o putem face și mai bună.

Faceți-l scalabil: funcții și extensii AppDriver

Apelurile directe API sunt puternice, dar nu ar trebui să vă aglomereze fișierele de testare. Când vă aflați că utilizați un anumit model de manipulare widget de mai multe ori, încapsulați-l într-o funcție de ajutor sau extindeți clasa AppDriver în sine.

Acest model vă face testele lizibile în timp ce vă centralizează logica interacțiunii widget. În loc să repetați apelurile API JavaScript în întreaga suită de testare, construiți o mică bibliotecă de metode de testare specifice domeniului care exprimă clar intenția.

In loc sa faci:

app$run_js(
  "$('#module-select select')(0).selectize.createItem('New Option');"
)

Ai putea defini o metodă într-un mod extins AppDriver:

ShinyDriver <- R6::R6Class(
  inherit = shinytest2::AppDriver,
  public = list(
    create_selectize_item = function(inputId, value) {
      self$run_js(sprintf(
        "$('#%s select')(0).selectize.createItem('%s');",
        inputId,
        value
      ))
    }
  )
)

Pentru o explorare mai profundă a acestui tipar, inclusiv exemple reale despre cum să structurați un model extins AppDriver cu metode personalizate, citiți ghidul de testare a caracteristicilor BDD Shiny, care demonstrează cum să construiți abstracții care să vă facă testele atât simple, cât și ușor de întreținut.

Găsirea apelurilor API JavaScript potrivite pentru widget-urile dvs. poate fi simplă dacă știți unde să căutați.

De obicei, documentația componentelor include link-uri către care bibliotecile JavaScript le alimentează.

  • shinyWidgets::pickerInput folosește Boostrap Select cu propriul său API documentat aici.
  • shinyWidgets::airDatePicker folosește Air Datepicker cu API-ul său documentat aici.

Doar mergeți acolo și vedeți dacă widget-ul expune metode care vă permit să îl manipulați programatic.


Cea mai fiabilă cale de testare a widget-urilor complexe nu este întotdeauna abstracția de cel mai înalt nivel.

Uneori, răspunsul se află la un nivel mai jos, în API-urile reale care alimentează aceste componente. Învățând să lucrezi direct cu API-urile JavaScript, câștigi atât flexibilitate, cât și stabilitate – iar testele tale devin mai rezistente la schimbările inevitabile care vin odată cu dezvoltarea UI.

Trecerea codului JavaScript în șiruri de caractere ar putea părea grosieră la început, dar cu o încapsulare și o abstractizare adecvată, puteți construi teste Shiny robuste, care să reziste testului timpului.

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.