Ultimamente, abbiamo ascoltato e letto sempre di più sullo sviluppo basato sui test. Questo dominio, tuttavia, viene fornito con una serie di espressioni e un gergo specifico che possono essere fonte di confusione per i nuovi arrivati. Questo articolo ti guiderà attraverso le definizioni più comuni, i tipi di test e le parti di test. Verranno forniti casi d'uso e, ove possibile, verrà presentato anche un codice in PHP.
Alcuni anni fa, un nuovo programmatore fu assunto da un team di sviluppo. Come ogni altro nuovo arrivato, era abbastanza confuso nel suo primo giorno. Mentre ascoltava le discussioni intorno a lui in ufficio, venivano usati molti termini specifici per i test. Queste erano espressioni sconosciute al nostro nuovo programmatore immaginario.
Fortunatamente per lui, essendo questo il suo primo giorno di lavoro, i due colleghi furono successivamente incaricati di spiegargli tutto questo gergo. Hanno iniziato con una lista di termini relativi al funzionamento interno di un caso di test.
Software di test del software è praticamente un test automatico. L'automazione del test è in circolazione da prima del PC; i primi framework di test automatici sono apparsi ai tempi dei mainframe e delle console. Oggi, i test automatici sono la strada più ovvia da fare. Perché? Perché il test è un compito noioso e ripetitivo, qualcosa che non si adatta bene agli esseri umani. I test automatizzati sono considerevolmente più veloci e più precisi dei test manuali. E no, non elimina il tester umano o una squadra di QA dallo schema. Semplicemente li fa fare un lavoro più umano e permettere loro di farlo bene.
Qualsiasi test dovrebbe essere suddiviso in quattro parti:
Progettiamo ogni test in modo da avere quattro fasi distinte che vengono eseguite in sequenza: setup di fixture, SUT esercizio, verifica dei risultati e fixture. - xUnit Test Patterns: Refactoring Test Code, di Gerard Meszaros
Un dispositivo rappresenta tutte le informazioni che il test necessita per essere esercitato. Un dispositivo può essere semplice come creare un oggetto semplice, come $ testedObject = new RealObject ();, o qualcosa di complicato come compilare database e avviare interfacce utente.
Tutto ciò che un sistema sotto test (SUT) deve avere in atto in modo da poter esercitare il SUT allo scopo di verificarne il comportamento. - xUnit Test Patterns: Refactoring Test Code di Gerard Meszaros
Probabilmente hai osservato questo termine ricorrente. I programmatori di solito si riferiscono ad esso come SUT. Rappresenta tutte le cose che devono essere testate. A seconda del tipo di test (vedi sotto per i tipi di test) il SUT può essere molte cose da un metodo o una classe all'intero sistema.
Qualunque cosa stiamo testando. Il SUT viene sempre definito dal punto di vista del test. - xUnit Test Patterns: Refactoring Test Code, di Gerard Meszaros
A partire dal secondo giorno di lavoro, il nostro programmatore ha scritto il suo primo test. Era più difficile di quanto si aspettasse. Per scriverlo, aveva bisogno di un quadro di prova, e ha dovuto creare un test case e poi eseguire tutto il metodi di prova. C'erano anche una manciata di strane dipendenze che aveva bisogno di capire. Sembrava che stessero imparando DOC era nei tempi previsti.
Un framework di testing è un'applicazione specificatamente progettata per testare il codice in una lingua specifica. Il concetto di a quadro di prova è stato pioniere da Kent Beck nei primi anni '90. Successivamente, il suo lavoro portò a un framework per SmallTalk, chiamato SmalltalkUnit, e in seguito rinominato Sunit.
Smalltalk ha sofferto perché mancava una cultura di prova. Questa colonna descrive una semplice strategia di test e un framework per supportarla. La strategia e il framework di testing non intendono essere soluzioni complete, ma piuttosto un punto di partenza da cui è possibile costruire strumenti e procedure di forza industriale. - Simple Smalltalk Testing: With Patterns, di Kent Beck
Era il primo xUnit quadro, e ha definito il concetto di base di test e i termini presentati sopra. Oggi, quasi tutti i linguaggi di programmazione offrono la sua versione di questo framework: PHPUnit per PHP, JUnit per Java, ShUnit per script di shell UNIX e così via. Sareste sorpresi di scoprire quante cose possono essere testate oggi e quanti test possono essere automatizzati.
Originariamente, un "test case" era definito come la più piccola unità di test di Kent Beck.
Quando parli con un tester, la più piccola unità di test di cui parlano è un caso di prova. TestCase è un oggetto utente, che rappresenta un singolo caso di test. - Simple Smalltalk Testing: With Patterns di Kent Beck
In questi giorni, stiamo usando metodo di prova per definire questa parte più piccola e un caso di test si riferisce principalmente a una serie di metodi di test correlati. Ad esempio, una situazione tipica è quando stiamo testando il nostro codice unitario e un caso di test si riferisce alla totalità dei metodi di test che testano una particolare classe o qualunque sia l'unità più piccola nel nostro linguaggio di programmazione. Un test case, molte volte, viene semplicemente chiamato: "un test."
Un metodo di prova è la parte più piccola di un'architettura di test. Un metodo di prova è l'unità che comprende le parti sopra definite: setup / exercise / verify / teardown. È la parte essenziale di qualsiasi test; quello che fa il lavoro.
Un metodo di prova è una procedura definitiva che produce un risultato del test. - Manuale di forma e stile, ASTM, 2012
Questo è stato facilmente uno dei termini più confusi per il nostro nuovo programmatore. Rappresenta tutte le altre classi e componenti di sistema di cui il nostro SUT ha bisogno per funzionare correttamente. Ma, anche, il DOC deve fornire metodi specifici che ci permettono di osservarlo e testarlo. I concetti di beffardo e prova raddoppia sono fortemente legati al DOC.
Una singola classe o un componente a grana grossa su cui dipende il sistema sotto test (SUT). La dipendenza è solitamente una delle deleghe tramite le chiamate ai metodi. - xUnit Test Patterns: Refactoring Test Code, di Gerard Meszaros
Poco dopo aver scritto i suoi primi test, il nuovo ragazzo si è reso conto che sta testando diverse parti logiche dell'applicazione. A volte, è meglio testare una piccola parte in isolamento; altre volte, è necessario testare un gruppo di oggetti insieme e il modo in cui parlano tra loro; e altre volte, è necessario testare l'intero sistema. Le cose sembravano più complicate di quanto si supponesse in precedenza; così il nostro programmatore andò avanti e leggere un libro, e un altro, e un altro, e, infine, capì.
La piramide di test è stata definita per la prima volta nel libro, Avere successo con lo sviluppo software agile con Scrum, di Mike Cohn, e poi presto adottato dalla comunità del software.
La piramide rappresenta i tre principali livelli di test: interfaccia utente, servizio e unità.
Il Strato dell'interfaccia utente rappresenta il livello di test più alto: quando il sistema viene esercitato attraverso l'interfaccia utente e l'intera applicazione viene testata come tale. Questo strato dovrebbe rappresentare la più piccola quantità nella nostra moltitudine di test.
Il Livello di servizio contiene diversi tipi di test. Si occupa principalmente della comunicazione interna dei moduli e del corretto funzionamento dell'API esterna (interfaccia di programmazione dell'applicazione) di un'applicazione. Dovrebbero esserci diversi test di questo tipo nelle nostre suite, ma non dovrebbero essere una base per i nostri test. Questi test di solito formano diverse parti dell'applicazione e, quindi, sono piuttosto lenti. Dovrebbero essere eseguiti il più frequentemente possibile, ma non su ogni salvataggio del codice. Probabilmente ad ogni build del sistema o quando avviene un commit al sistema di controllo delle versioni.
Il Strato unitario si riferisce a test che esercitano le più piccole unità del nostro codice in completo isolamento. Questi test dovrebbero rappresentare la stragrande maggioranza dei test. Dovrebbero essere molto veloci (1-4 millisecondi / test) e dovrebbero essere eseguiti il più frequentemente possibile. Lo sviluppo guidato dai test (TDD) è un buon esempio di come massimizzare l'uso dei test unitari.
Sulla base dell'esempio precedente, la comunità ha ideato diverse versioni più dettagliate della piramide di test. Quello che considero il migliore si può vedere nell'immagine qui sotto.
I tre strati principali possono essere chiaramente distinti, ma lo strato centrale è più dettagliato. Col passare del tempo, la comunità del software ha scoperto e definito numerosi nuovi metodi di test. Alcuni di questi erano inclusi nella piramide.
Notare che: le tecniche e i framework di test automatici stanno ancora cambiando molto velocemente. Questo è il motivo per cui, come puoi vedere di seguito, alcune espressioni non sono ancora chiare e ci sono diversi termini per le stesse definizioni a seconda della comunità che li ha promossi.
Un test unitario rappresenta il test dell'unità più piccola che il linguaggio di programmazione consente. Nella programmazione orientata agli oggetti, queste sono classi / oggetti. In altre lingue, possono essere piccoli moduli o persino funzioni / procedure.
UN test in queste definizioni si riferisce alla stessa cosa rappresentata da un caso di test.
Un test che verifica il comportamento di una piccola parte del sistema generale. Ciò che trasforma un test in un test unitario è che il sistema sotto test (SUT) è un sottogruppo molto piccolo del sistema generale e potrebbe essere irriconoscibile per qualcuno che non è coinvolto nella creazione del software. - xUnit Test Patterns: Refactoring Test Code di Gerard Meszaros
I test unitari rappresentano la stragrande maggioranza dei test che un programmatore scrive. Sì, è vero: i test di unità sono scritti per la maggior parte del tempo dai programmatori. Il test unitario aiuta i programmatori a sviluppare l'applicazione, a prevenire errori comuni, errori di battitura e regressioni. Sono prove fatte da programmatori per programmatori.
Questo è il motivo per cui i test unitari sono di natura più tecnica e più criptica. Sono qui per aiutare i programmatori a scrivere un codice migliore; quando qualcosa non riesce a livello di test unitario, di solito è un problema da risolvere per un programmatore.
Come suggerisce il nome, i test dei componenti sono scritti per blocchi un po 'più grandi dell'applicazione. Un test sui componenti di solito esercita un intero modulo o un gruppo di unità logicamente interdipendenti.
Il componente è una conseguenza di una o più decisioni progettuali, sebbene il suo comportamento possa anche essere ricondotto ad alcuni aspetti dei requisiti. - xUnit Test Patterns: Refactoring Test Code di Gerard Meszaros
Sicuramente, un test dei componenti esercita più codice di un test unitario. Può anche verificare come alcune unità lavorano insieme e parlano tra loro.
Un componente può anche essere ricondotto a un requisito oa una parte di un requisito. Ciò significa che un test dei componenti non è solo per i programmatori. Team leader, maestri di mischia, architetti e altre persone tecnicamente coinvolte sono sicuramente interessati ai moduli, alla loro organizzazione e talvolta anche al loro funzionamento interiore. Queste persone non hanno bisogno di familiarità con un linguaggio di programmazione specifico. Il test deve concentrarsi maggiormente sul comportamento e definire le aspettative in un modo più comprensibile.
Ad esempio, un test unitario può avere un messaggio di errore che indica che:
TestFileAccessCanWriteToAFile: impossibile affermare che il file '/ tmp / testfile' sia presente sul sistema.
Tale messaggio non sarebbe utile per un architetto, un manager o un caposquadra. Un test dei componenti potrebbe fallire con un errore più descrittivo:
Test di amministrazione dell'account: non riuscito quando abbiamo cercato di specificare 0 (zero) come il denaro totale che un utente ha nel suo account.
Tale test esercita una funzionalità di livello superiore. In base al messaggio di errore sopra riportato, potrebbero esserci diversi livelli di comunicazione e classi / oggetti coinvolti nell'operazione di specificare un importo come totale nel conto di alcuni.
Questo tipo di test richiede diversi moduli e controlla come si integrano tra loro. Verifica se le API del modulo interno sono compatibili e funzionano come previsto.
Il termine, tuttavia, consente una vasta gamma di possibili usi. Alcune comunità di software mettono in relazione fortemente i test di integrazione con test su come la nostra applicazione funziona all'interno del mezzo che deve eseguire. In altre parole, come si integra nel sistema superiore.
Altri definiscono il test di integrazione a diversi livelli: tutto ciò che definisce la comunicazione tra due elementi può essere visto come un'integrazione. Questi elementi possono essere unità, come classi, moduli o parti funzionali ancora superiori del software.
Non esiste una definizione unanimemente accettata per il termine, test di integrazione.
La GUI di un'applicazione sta parlando al software tramite l'API del software. I test a questo livello esercitano un sacco di codice e possono richiedere un periodo di tempo relativamente significativo.
L'API è il mezzo con cui altri software possono richiamare alcune funzionalità. - xUnit Test Patterns: Refactoring Test Code di Gerard Meszaros
Nella programmazione orientata agli oggetti, tali API sono definite dai metodi pubblici delle classi. Tuttavia, se diamo un'occhiata a uno schema di progettazione architettonica di alto livello, il significato dell'API può essere limitato ai metodi pubblici delle classi che forniscono funzionalità attraverso i confini della logica aziendale. Queste classi limite rappresentano l'API e dovremmo verificare che, quando le chiamiamo e le usiamo, il sistema si comporti come previsto.
Di solito, questi test vengono eseguiti periodicamente e richiedono molto tempo per terminare.
Ci dovrebbero essere solo alcuni rari casi quando si desidera testare la presentazione di un'applicazione. Non c'è davvero alcuna logica nel livello della GUI, solo la presentazione.
Verifica se un pulsante è verde o rosso o se lo è 30px
in larghezza è inutile, ed è troppo di un overhead. Quindi non saltare a testare le tue opinioni. Se qualcosa va terribilmente male con la GUI, sarà osservato nella fase di test manuale esplorativo.
Le viste di test dovrebbero fare solo due cose: test della presentazione condizionale e testare che l'API prevista venga chiamata.
Saltare a testare i tuoi punti di vista può essere allettante. Non farlo! Prova solo ciò che pensi possa fallire. Test solo per valori o chiamate di funzione. Non verificare mai gli elementi della GUI o le loro proprietà. Utilizzare REGEX quando possibile per far corrispondere le stringhe e controllare le parole chiave che non è probabile che cambino.
Ad esempio, il seguente pseudo codice è un cattivo esempio per testare la presenza di una stringa sullo schermo.
function testItCanTellTheNameOfTheUser () // qualche logica di codice di rendering qui $ renderedName = $ this-> renderName (); $ this-> assertEquals ('L'utente ha il nome'. $ renderedName ['first']. ". $ renderedName ['last']. '.');
Non solo questo test è difficile da leggere, ma verifica una frase esatta - qualcosa di simile "L'utente ha il nome John Doe.", compresa la punteggiatura! Perché questo è cattivo? Perché qualcuno potrebbe cambiare facilmente la forma di questa frase senza cambiarne il significato.
Cosa succede se il nostro cliente richiede Cognome nome modulo da presentare? Ciò renderebbe il nostro test fallire. Dobbiamo chiederci: il test fallirà? Abbiamo cambiato la logica del software? Dico no, non dovrebbe fallire. Il nome sarebbe ancora presente sullo schermo; l'ordine delle due parti sarebbe semplicemente diverso. Questo è un test più appropriato.
function testItCanTellTheNameOfTheUser () // qualche logica di codice di rendering qui $ renderedName = $ this-> renderName (); $ this-> assertRegExp ($ renderedName ['first'], $ renderedName); $ this-> assertRegExp ($ renderedName ['last'], $ renderedName);
Questo ora assicura che il nome sia presente, ma non si preoccupa del costrutto lessicale. Qualcuno potrebbe cambiare la frase iniziale in qualcosa, come Don, John è il nome dell'utente corrente. Il significato rimarrà lo stesso e il test passerà ancora correttamente!
Dopo circa un mese di lavoro, il nostro nuovo programmatore immaginario si rende conto che, anche se la piramide è piuttosto interessante, non è completa. A volte, ci sono un paio di test diversi che dovrebbero essere eseguiti - e sono piuttosto difficili da posizionare sulla piramide.
Questi sono uno dei test più controversi. A seconda del tipo di libri che stai leggendo, i test di accettazione potrebbero essere indicati come Test funzionali
o
o
o
Ogni nome proviene da un'altra comunità o autore. Io personalmente preferisco Test di accettazione o Test end-to-end.
Un test di accettazione verifica il comportamento di una porzione della funzionalità visibile dell'intero sistema. - xUnit Test Patterns: Refactoring Test Code di Gerard Meszaros
Tale test farà qualcosa sulla GUI. Il cambiamento avverrà nell'intero sistema. I dati verranno salvati nel database o nel file system. La comunicazione di rete sarà fatta. Infine, la GUI verrà controllata per la risposta dal sistema. Tali test tentano di imitare completamente un utente.
I test di accettazione sono strettamente correlati agli stakeholder della nostra applicazione. Di solito sono definiti nella lingua del business e, quando qualcosa va storto, un'intera funzionalità viene considerata defunta. Questi test vengono anche utilizzati per definire la funzionalità di alto livello dell'applicazione.
Di solito, sono scritti da QA e gestione e implementati dai programmatori. Originariamente, sono stati inventati come un ponte tra gestione e produzione. Per alcune situazioni, ci sono riusciti. Il linguaggio dei test è abbastanza flessibile da essere scritto e compreso da persone non direttamente coinvolte nella scrittura del software.
Esistono strutture speciali per tali test, come Fitness, Selenio, Watir, Cetriolo e altri.
Questi sono un caso più speciale e non vengono usati troppo spesso. A volte è possibile utilizzarli in linguaggi object oriented quando è necessario testare interfacce ed eredità. Il test fondamentalmente garantisce che una classe implementa realmente tutte le interfacce necessarie.
I Test del contratto spiegano come una classe dovrebbe estendere una superclasse o implementare un'interfaccia. - J. B. Rainsberger
In alcune applicazioni, il termine contrarre è usato per un altro tipo di test. Questa seconda definizione del test contrattuale verifica il rispetto del contratto tra la nostra applicazione e un componente di terzi da cui dipendiamo. Questi test esercitano il codice corrente e il codice di terze parti assicurando che i risultati siano come previsto.
Dopo una meritata vacanza, il nostro programmatore non-junior è tornato al lavoro. Era il suo primo congedo e si sente pieno di nuovi poteri per scrivere test e codice. Dopo sei mesi, si sente abbastanza bene al lavoro; si è integrato bene nella squadra e scrive un codice veramente buono. Ma di tanto in tanto, ha una sensazione frustrante. Eseguire cinque diversi tipi di suite di test in un ordine rigorosamente definito ogni sera è noioso e soggetto a errori.
Poi, c'è un'altra strana discussione tra il suo capo squadra e il management. Stanno parlando di C.I. e CD.. Cosa potrebbero significare? Era troppo criptico per il nostro nuovo programmatore capire. Qualche settimana dopo, c'era un messaggio aziendale: "Per favore non eseguire più i test serali. Abbiamo C.I.!. Per saperne di più, è andato dal suo caposquadra e ha chiesto: "Cos'è CI e CD?".
I team che fanno affidamento su test automatici hanno bisogno di un modo per eseguire tutti questi test in modo organizzato ed efficiente. Un sistema di integrazione continua aiuta con questo.
L'integrazione continua è una pratica di sviluppo del software in cui i membri di un team integrano il loro lavoro frequentemente, di solito ogni persona si integra almeno giornalmente, portando a più integrazioni al giorno. Ogni integrazione viene verificata da una build automatizzata (incluso test) per rilevare gli errori di integrazione il più rapidamente possibile. - Martin Fowler
Sulla base di questa definizione, il processo di integrazione continua farà il lavoro di eseguire i nostri test senza l'intervento umano. Nella definizione, l'integrazione frequente è esemplificata come giornaliera, ma posso dirvi che è davvero interessante lavorare su una base software che viene automaticamente testata su ogni commit. Commettere frequentemente significa che tutte le modifiche completate devono essere eseguite in modo da poter avere decine di commit in un solo giorno. Ogni commit attiverà un test completo del sistema e riceverai una bella e-mail, verde o rossa, a seconda del risultato, in pochi minuti. Se la posta è verde, il prodotto è teoricamente immediatamente spedibile.
Questo non è così correlato al testing, ma a CI. Mentre CI ti consente di disporre di un sistema di consegna, i rilasci vengono eseguiti periodicamente e manualmente.
La consegna continua automatizza tutti i passaggi dal codice al cliente.
Dopo tutto è fatto a livello di CI, la consegna continua fa un ulteriore passo avanti e crea i kit di installazione del software, li pubblica a seconda delle necessità e può anche attivare procedure di aggiornamento remoto sui client. L'obiettivo di tale sistema è consegnare il prodotto il più rapidamente possibile all'utente finale. Dipende molto dai test automatici e, se tutti passano, il prodotto viene consegnato. Periodo.
Anche se, in alcune situazioni, questo può sembrare molto interessante, nella maggior parte delle applicazioni, è ancora troppo pericoloso. Di solito, qualsiasi sistema critico deve essere sottoposto a una sessione di test manuale esplorativa prima della consegna.
Ci sono parti di una procedura di test che sono semplicemente troppo difficili - se non impossibili - per automatizzare. Ecco perché Test manuale esplorativo la sessione viene solitamente eseguita prima di ogni versione del software. Questi test possono essere eseguiti da tester specializzati o dai programmatori a seconda della struttura del team e dell'azienda.
Il test manuale coinvolge l'immaginazione. Gli esseri umani stanno frugando il sistema assicurandosi che funzioni e sembri come desiderato.
Alcuni team considerano il test manuale a << Break it if you can! >> concetto.
Non puoi evitare di testare il gergo. Si spera che questo articolo abbia fatto luce sulle differenze tra le varie forme di test. Qualsiasi domanda? Chiedi sotto!