Come scrivere test di unità mantenibili, non fragili, per una GUI?


16

Ho provato a scrivere test unit UI per le mie app GUI e ho riscontrato il problema che, mentre funzionano bene quando le scrivo inizialmente, risultano fragili e si rompono ogni volta che cambia il design (cioè abbastanza spesso). Faccio fatica a trovare una serie di linee guida che mi porterebbero ad avere unit test 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 HTML). I test che verificano uno stato specifico di una parte specifica del componente sono generalmente fragili. I test che vanno come click-click-click-prevedono, che cercano di seguire il comportamento dell'utente e la logica aziendale sottostante (che è la parte più importante) di solito risultano fragili. Come scrivo buoni test?


Per essere più precisi, vorrei conoscere alcuni schemi su cosa avrei potuto testare nella mia UI, non esattamente su come testarlo. Convenzioni di denominazione e identificatori fissi sono buoni, ma non risolvono il problema principale, ovvero che le GUI cambiano molto. Mi piacerebbe testare i comportamenti che difficilmente cambieranno. Come trovare la cosa giusta da testare?


1
@MichaelDurrant: hai modificato la domanda in merito al test dell'interfaccia utente in generale, mentre inizialmente avevo chiesto il test dell'unità. Trovo i test di integrazione più difficili da mantenere e preferisco i test unitari su di essi, quando possibile.
mik01aj,

2
Penso che questo sia parte del problema, fondamentalmente non puoi davvero testare nessuna interfaccia da soli test di unità poiché la loro ragion d'essere è l'interfaccia con qualcosa. Le GUI non sono diverse sotto questo aspetto.
jk.

m01, puoi cambiarlo indietro. Penso ai test dell'interfaccia utente come ai test integrati. I test tendono a fare affidamento sui dati seed e sul dispositivo presenti e sull'interfaccia utente che funziona con essi. Se hai veri test dell'interfaccia utente che non si basano su altri dati eccellenti. Tuttavia ho trovato che questo è relativamente raro.
Michael Durrant,

2
correlati ma non un duplicato poiché si tratta di test gui: programmers.stackexchange.com/questions/109703/…
k3b

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 un cambiamento nella GUI che non è un cambiamento nei requisiti . Dovresti cercare di strutturare il tuo codice di test in modo tale che una modifica nella GUI sia isolata in una singola posizione nei test.

Ad esempio, considera un test che è formulato come:

Quando l'utente immette "999" nel campo numero di telefono e fa clic sul pulsante Salva, il messaggio di errore dovrebbe indicare "numero di telefono non valido".

Qui c'è molto spazio perché questo test si interrompa quando si rielabora l'interfaccia, anche se il requisito per la convalida rimane.

Ora, mettiamolo in una piccola formulazione alternativa:

Quando l'utente inserisce "999" come numero di telefono e salva la pagina del profilo, dovrebbe mostrare un errore che dice "numero di telefono non valido"

Il test è lo stesso, i requisiti sono gli stessi, ma questo tipo di test sopravviverà a un restyling dell'interfaccia utente. Dovrai cambiare il 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 logica di convalida della visualizzazione degli errori da javascript-avvisi a jquery-popups, ad esempio, devi solo cambiare la singola parte di test che controlla i messaggi di errore.


4

Questo è un problema comune Presterei attenzione a:

  • Come si nominano gli elementi

    Utilizzare css id o class per identificare gli elementi. Favorire l'utilizzo dell'ID CSS quando l'oggetto è univoco. Considera il framework che stai utilizzando, ad esempio con Ruby on Rails l' nameattributo viene assegnato automaticamente e può (non intuitivamente) essere migliore dell'uso dell'id css o della classe

  • Come identifichi gli elementi.

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

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

  • Evita qualifiche non necessarie per gli elementi. Potresti trovarti utilizzando quanto segue:

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

    Tuttavia, ciò richiede che quel campo di input rimanga in una forma con un ID esatto del veicolo e in una pagina con una carrozzeria che ha una classe di main e un div con un ID di veicoli che ha un figlio immediato di una forma con un ID di veicolo. Qualsiasi modifica a una qualsiasi di quella struttura e il test si interrompe. In questo caso potresti trovarlo

    input#primary_vehicle_name

    è sufficiente per identificare in modo univoco l'elemento.

  • Evita i test che fanno riferimento 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 utilizzare identificatori come ID CSS e classe CSS o attributi di dati. Elementi come form, inpute selectutilizzati nelle forme sono anche buone parti di elementi identificativi, di solito in combinazione con id o classe, ad esempio, li.vehicleo input#first-vehicle È inoltre possibile aggiungere i propri identificativi, ad esempio <div data-vehicle='dodge'>. In questo modo è possibile evitare di utilizzare gli ID elemento o le classi, che possono essere cambiati da sviluppatori e designer. Nel corso del 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 che un selettore in linea hard codificato identifichi i valori a favore degli oggetti della pagina - piccoli pezzi di testo che sono conservati in variabili o metodi e quindi possono essere riutilizzati e mantenuti anche 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";`  
    

    Maggiori informazioni sugli oggetti della pagina su selenium wiki e selenium docs

  • Comunicazione con gli sviluppatori.

    Indipendentemente dall'approccio tecnico in termini di "sviluppatori apportano modifiche e rompono l'automazione del controllo qualità" che è un problema di flusso di lavoro. Devi assicurarti che: tutti siano la stessa squadra; lo sviluppatore esegue gli stessi test integrati; gli standard sono concordati e seguiti da entrambi i gruppi; la definizione di done include l'esecuzione e l'eventuale aggiornamento dei test dell'interfaccia utente; gli sviluppatori e i tester si accoppiano sui piani di test ed entrambi partecipano alla preparazione dei biglietti (se agili) e parlano dei test dell'interfaccia utente come parte della preparazione. Dovresti assicurarti che qualsiasi approccio e strategia tu usi per nominare sia coordinato con gli sviluppatori di applicazioni. Se non arrivi sulla stessa pagina, ti piacerà lo scontro sulla denominazione degli oggetti. Alcuni esempi di metodi di oggetti 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 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]']";
    

2
Questi sono suggerimenti utili e in realtà li seguo già quasi tutti. Il mio problema era che scrivo i test per alcuni pulsanti, quindi questo pulsante viene rimosso mentre la stessa azione viene gestita da qualche altra parte. Oppure il pulsante rimane lì, ma l'etichetta cambia e il pulsante esegue anche qualche azione aggiuntiva.
mik01aj,

1
Ah, è buono a sapersi. Sì, per quella roba mi concentrerei sul flusso di lavoro e sull'interazione organizzativa di qa-sviluppatore
Michael Durrant,

1
Sto anche pubblicando informazioni per gli altri che trovano la tua domanda e potrebbero non avere tutti i pezzi sul posto che hai o potrebbero non conoscerli.
Michael Durrant,

1
Ho ampliato il mio ultimo punto in base al tuo feedback.
Michael Durrant,

1
E ho aggiornato le parti sulla denominazione e l'identificazione degli elementi e sul non usare testo visibile.
Michael Durrant,

3

Il motivo per cui le persone hanno sviluppato prima cose come MVC, MVP e presentatore e modelli di progettazione simili, è stato quello di separare la logica aziendale dall'interfaccia utente.

Ovviamente, la parte della vista può essere testata solo avviando il programma e controllando ciò che mostra - in altre parole, può essere testata 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.


Le GUI cambiano molto

Questo può succedere solo quando hai cambiato 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, il cambiamento non si propagherà in molti luoghi.


2
Penso che sia un buon punto. Forse sto solo sbagliando MVC :) A proposito, credo che cambiare i requisiti sia inevitabile quando sviluppi una piattaforma web per molti utenti. Non sai come si comporteranno i tuoi utenti fino a quando non inizieranno a usare la GUI.
mik01aj,

2

I test di interazione con la GUI non dovrebbero essere più o meno fragili di qualsiasi altro tipo di test. Questo è; se l'applicazione sta cambiando in qualche modo, i test devono essere aggiornati per riflettere ciò.

Come confronto:

Test unitario

Originale : validateEmail()dovrebbe generare InvalidDataun'eccezione. Che è correttamente coperto nel test unitario.

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

Test GUI

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

Modifica : l'immissione di un'e-mail non valida comporterà un errore inline contenente "E-mail immessa non valida". Ora il test non è corretto, lo aggiorni e tutto è di nuovo verde.


Ricorda che stai testando input e output - alcuni comportamenti ben definiti. Indipendentemente dal fatto che si tratti di un test GUI o di un test unitario o di un test di integrazione, ecc.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.