Come scrivere test unitari mantenibili, non fragili, per una GUI?

16

Ho provato a scrivere i test delle unità dell'interfaccia utente per le mie app GUI e affronto il problema che, mentre funzionano bene quando inizialmente li scrivo, risultano essere fragili e si rompono ogni volta che il progetto cambia (cioè, molto spesso) . Sto faticando a trovare una serie di linee guida che mi portino ad avere test unitari mantenibili per la GUI.

Per ora, una cosa che ho scoperto è che i test che dicono "questo componente dovrebbe mostrare i suoi dati di input da qualche parte" sono buoni (e questo è molto facile con l'HTML). I test che controllano lo stato specifico di una parte specifica del componente sono solitamente fragili. I test vanno come click-click-click-expect, che cercano di seguire il comportamento dell'utente e la logica di business sottostante (che è la parte più importante) di solito si rivela fragile. Come scrivere buoni test?

Per essere più precisi, mi piacerebbe conoscere alcuni schemi su che cosa potrei provare nella mia interfaccia utente, non esattamente come per testarlo. Le convenzioni di denominazione e gli identificatori fissi sono buoni, ma non risolvono il problema principale, ovvero le GUI cambiano molto. Mi piacerebbe testare i comportamenti che è improbabile che cambi. Come trovare la cosa giusta da testare?

    
posta mik01aj 19.10.2015 - 11:59
fonte

4 risposte

3

Un problema comune con i test della GUI ... Il motivo principale per cui questi test sono considerati fragili è perché non possono sopravvivere a una modifica della GUI che non è una modifica dei requisiti . Dovresti sforzarti di strutturare il tuo codice di test in modo tale che una modifica nella GUI sia isolata in un singolo posto nei test.

Ad esempio, prendi in considerazione un test redatto come:

When the user enters '999' into the phonenumber field and clicks the save button, the error message popup should say 'invalid phone number'.

Un sacco di spazio qui perché questo test si interrompa quando rielabori l'interfaccia, anche se il requisito per la convalida rimane.

Ora, mettiamola in una formulazione alternativa:

When the user enters '999' as a phonenumber and saves the profile page, it should show an error that says 'invalid phone number'

Il test è lo stesso, i requisiti sono gli stessi, ma questo tipo di test sopravviverà a un restyling dell'interfaccia utente. Dovrai cambiare codice, ovviamente, ma il codice sarà isolato. Anche se hai dieci o venti di questi test per la pagina del tuo profilo e sposti la tua logica di convalida di visualizzazione di errore da javascript-alerts a jquery-popups, ad esempio, devi solo modificare la singola parte di test che controlla i messaggi di errore.

    
risposta data 19.10.2015 - 18:17
fonte
4

Questo è un problema comune. Vorrei prestare attenzione a:

  • Come nominare gli elementi

    Usa ID css o classe per identificare gli elementi. Preferisci usare l'ID CSS quando l'oggetto è unico. Considera il framework che stai utilizzando, ad esempio con Ruby on Rails l'attributo name viene assegnato automaticamente e può (non intuitivamente) essere migliore dell'uso dell'id del css o della classe

  • Come identifichi gli elementi.

    Evita gli identificatori posizionali come table/tr/td/td a favore di forme come td[id="main_vehicle" o td[class='alternates'] . Prendi in considerazione l'utilizzo degli attributi dei dati quando appropriato. Ancora meglio, cerca di evitare tag di layout come <td> del tutto, quindi per quanto sopra potresti aggiungere uno span e usarlo, ad es. <span id="main_vehicle"> o un selettore di caratteri jolly come *[id="main_vehicle"] in cui * potrebbe ora essere div, span, td, ecc.

  • Utilizzo di attributi dei dati specifici per test utilizzati solo per qa e test.

  • Evitare qualifiche non necessarie per gli elementi. Potresti trovarti ad usare quanto segue:

    body.main div#vehicles > form#vehicle input#primary_vehicle_name

    Tuttavia questo richiede che il campo di input rimanga in un modulo con un ID esatto del veicolo e su una pagina con un corpo che ha una classe di principale e un div con un id di veicoli che ha un figlio immediato di un modulo con un id del veicolo. Qualsiasi modifica a qualsiasi di quella struttura e le interruzioni di prova. In questo caso potresti trovare che

    input#primary_vehicle_name

    è sufficiente per identificare univocamente l'elemento.

  • Evita i test che si riferiscono al testo visibile. Il testo sulla pagina che viene mostrato all'utente di solito cambia nel tempo man mano che il sito viene mantenuto e aggiornato, quindi utilizza identificatori come ID css e classe css o attributi dati. Elementi come form , input e select utilizzati nei moduli sono anche buone parti degli elementi identificativi, solitamente in combinazione con l'id o la classe, ad es. li.vehicle o input#first-vehicle Puoi anche aggiungere i tuoi identificatori personali, ad es. %codice%. In questo modo puoi evitare di utilizzare gli ID o le classi dell'elemento, che potrebbero essere modificati da sviluppatori e designer. In realtà, nel tempo ho scoperto che è meglio lavorare solo con sviluppatori e designer e raggiungere un accordo su nomi e ambiti. È difficile.

  • Come vengono mantenuti i dati fissi.

    Simile all'identificazione di elementi reali, cerca di evitare i valori di identificazione del selettore codificato in linea a favore degli oggetti della pagina: parti di testo contenute in variabili o metodi, che possono quindi essere riutilizzate e mantenute centralmente. Esempi di variabili javascript che seguono questo modello per valori hardcoded:

    storedVars["eqv_auto_year"] = "2015";
    storedVars["eqv_auto_make_1"] = "ALFA ROMEO";
    storedVars["eqv_auto_make_2"] = "HONDA";'  
    

    Ulteriori oggetti sulla pagina wiki del selenio e selenium docs

  • Comunicazione con gli sviluppatori.

    Indipendentemente dall'approccio tecnico in termini di "sviluppatori che apportano modifiche e interrompono l'automazione del controllo qualità", si tratta di un problema di flusso di lavoro. Devi assicurarti che: tutti sono la stessa squadra; lo sviluppatore esegue gli stessi test integrati; gli standard sono concordati e seguiti da entrambi i gruppi; la definizione di fatto include l'esecuzione e l'eventuale aggiornamento dei test dell'interfaccia utente; gli sviluppatori e i tester si confrontano sui piani di test e partecipano entrambi al grooming dei ticket (se Agile) e parlano di test dell'interfaccia utente come parte del grooming. Dovresti assicurarti che qualunque approccio e strategia tu usi per la denominazione sia coordinata con gli sviluppatori di applicazioni. Se non saliti sulla stessa pagina ti piacerà lo scontro sulla denominazione degli oggetti. Alcuni esempi di metodi di oggetti di pagina che ho creato di recente per un progetto ruby:

    def css_email_opt_in_true
      'auto_policy[email_opt_in][value=1]'
    end 
    
    def css_phone_opt_in
      '*[name="auto_policy[phone_opt_in]"]'
    end 
    
    def css_phone_opt_in_true
      'input[name=phone_opt_in][value=true]'
    end 
    
    def css_credit_rating
      'auto_policy[credit_rating]'
    end
    

    Ecco gli stessi oggetti della pagina delle variabili javascript:

    storedVars["css_email_opt_in"] = "css=*[name='auto_policy[email_opt_in]']";
    storedVars["css_phone_opt_in"]="css=*[name='auto_policy[phone_opt_in]']";
    storedVars["css_phone_opt_in_true"]="css=input[name='phone_opt_in'][value=true]";
    storedVars["css_credit_rating"]="css=select[name='auto_policy[credit_rating]']";
    
risposta data 19.10.2015 - 12:25
fonte
3

Il motivo per cui le persone hanno sviluppato cose come MVC, MVP e presentatore in primo luogo, e modelli di progettazione simili, era di separare la logica di business dall'interfaccia utente.

Ovviamente, la parte vista può essere testata solo avviando il programma e controllando ciò che mostra - in altre parole, può essere testato solo nei test di accettazione.

D'altra parte, testare la logica di business può essere fatto in unit test. E questa è la risposta alla tua domanda. Prova tutto nel modello e se puoi e vuoi, puoi anche testare il codice del controller.

GUIs change a lot

Questo può accadere solo quando si cambiano i requisiti. Quando un requisito cambia, non c'è modo di aggirarlo, tranne che per modificare il codice. Se riesci a creare un buon design e architettura, la modifica non si propagherà in molti punti.

    
risposta data 19.10.2015 - 14:10
fonte
2

I test di interazione della GUI non dovrebbero essere più o meno fragili di altri tipi di test. Questo è; se la tua applicazione sta cambiando in qualche modo, i test devono essere aggiornati per riflettere questo.

Come confronto:

Test unità

Originale : validateEmail() dovrebbe generare un'eccezione InvalidData . Che è correttamente coperto nel test dell'unità.

Modifica : validateEmail() dovrebbe generare un'eccezione InvalidEmail . Ora il tuo test non è corretto, lo aggiorni e tutto è di nuovo verde.

Test della GUI

Originale : l'inserimento di un'e-mail non valida comporterà una finestra di errore popup contenente "Dati non validi inseriti". Rilevato correttamente dai test.

Modifica : l'inserimento di un'e-mail non valida comporterà un errore in linea contenente "E-mail non valida inserita". Ora il tuo test non è corretto, lo aggiorni e tutto è di nuovo verde.

Ricorda che stai testando ingressi e uscite: alcuni comportamenti ben definiti. Indipendentemente dal fatto che si tratti di un test della GUI o di un test unitario o di un test di integrazione, ecc.

    
risposta data 23.10.2015 - 00:13
fonte