8 Migliori pratiche di test automatizzati per un'esperienza di test positiva
Pubblicato: 2022-03-11Non c'è da stupirsi che molti sviluppatori considerino i test come un male necessario che consuma tempo ed energia: i test possono essere noiosi, improduttivi e completamente troppo complicati.
La mia prima esperienza con i test è stata terribile. Ho lavorato in un team che aveva severi requisiti di copertura del codice. Il flusso di lavoro era: implementare una funzionalità, eseguirne il debug e scrivere test per garantire la copertura completa del codice. Il team non aveva test di integrazione, solo unit test con tonnellate di mock inizializzati manualmente e la maggior parte dei test unitari testava mappature manuali banali mentre utilizzava una libreria per eseguire mappature automatiche. Ogni test ha cercato di affermare ogni proprietà disponibile, quindi ogni modifica ha infranto decine di test.
Non mi piaceva lavorare con i test perché erano percepiti come un onere dispendioso in termini di tempo. Tuttavia, non mi sono arreso. I test di affidabilità forniti e l'automazione dei controlli dopo ogni piccola modifica hanno suscitato il mio interesse. Ho iniziato a leggere e ad esercitarmi e ho imparato che i test, se fatti bene, potevano essere sia utili che divertenti.
In questo articolo, condivido otto best practice per i test automatizzati che avrei voluto conoscere dall'inizio.
Perché hai bisogno di una strategia di test automatizzata
I test automatizzati sono spesso focalizzati sul futuro, ma quando li implementi correttamente, ne trai vantaggio immediato. L'utilizzo di strumenti che ti aiutano a svolgere meglio il tuo lavoro può farti risparmiare tempo e rendere il tuo lavoro più piacevole.
Immagina di sviluppare un sistema che recupera gli ordini di acquisto dall'ERP dell'azienda e li invia a un fornitore. Hai il prezzo degli articoli precedentemente ordinati nell'ERP, ma i prezzi attuali potrebbero essere diversi. Vuoi controllare se effettuare un ordine a un prezzo inferiore o superiore. Hai le preferenze dell'utente memorizzate e stai scrivendo il codice per gestire le fluttuazioni dei prezzi.
Come verificheresti che il codice funzioni come previsto? Probabilmente:
- Crea un ordine fittizio nell'istanza dello sviluppatore dell'ERP (supponendo che tu lo abbia configurato in anticipo).
- Esegui la tua app.
- Seleziona quell'ordine e avvia il processo di immissione dell'ordine.
- Raccogliere dati dal database dell'ERP.
- Richiedi i prezzi correnti dall'API del fornitore.
- Sovrascrivi i prezzi nel codice per creare condizioni specifiche.
Ti sei fermato al punto di interruzione e puoi andare passo dopo passo per vedere cosa accadrà per uno scenario, ma ci sono molti scenari possibili:
| Preferenze | Prezzo ERP | Prezzo del venditore | Dovremmo effettuare l'ordine? | |
|---|---|---|---|---|
| Consenti un prezzo più alto | Consenti prezzo più basso | |||
| falso | falso | 10 | 10 | vero |
| (Qui ci sarebbero altre tre combinazioni di preferenze, ma i prezzi sono uguali, quindi il risultato è lo stesso.) | ||||
| vero | falso | 10 | 11 | vero |
| vero | falso | 10 | 9 | falso |
| falso | vero | 10 | 11 | falso |
| falso | vero | 10 | 9 | vero |
| vero | vero | 10 | 11 | vero |
| vero | vero | 10 | 9 | vero |
In caso di bug, l'azienda potrebbe perdere denaro, danneggiare la sua reputazione o entrambi. È necessario controllare più scenari e ripetere il ciclo di controllo più volte. Farlo manualmente sarebbe noioso. Ma i test sono qui per aiutare!
I test ti consentono di creare qualsiasi contesto senza chiamate ad API instabili. Eliminano la necessità di fare clic ripetitivi attraverso interfacce vecchie e lente che sono fin troppo comuni nei sistemi ERP legacy. Tutto quello che devi fare è definire il contesto per l'unità o il sottosistema e quindi qualsiasi debug, risoluzione dei problemi o esplorazione dello scenario avviene istantaneamente: esegui il test e torni al tuo codice. La mia preferenza è impostare una combinazione di tasti nel mio IDE che ripeta la mia precedente esecuzione di test, fornendo un feedback immediato e automatizzato mentre apporto le modifiche.
1. Mantieni il giusto atteggiamento
Rispetto al debug manuale e all'autotest, i test automatizzati sono più produttivi sin dall'inizio, anche prima del commit del codice di test. Dopo aver verificato che il tuo codice si comporti come previsto, testando manualmente o forse, per un modulo più complesso, eseguendo un debugger durante il test, puoi utilizzare le asserzioni per definire cosa ti aspetti per qualsiasi combinazione di parametri di input.
Con il superamento dei test, sei quasi pronto per impegnarti, ma non del tutto. Preparati a refactoring del codice perché la prima versione funzionante di solito non è elegante. Eseguiresti quel refactoring senza test? È discutibile perché dovresti completare di nuovo tutti i passaggi manuali, il che potrebbe diminuire il tuo entusiasmo.
E il futuro? Durante l'esecuzione di qualsiasi refactoring, ottimizzazione o aggiunta di funzionalità, i test aiutano a garantire che un modulo si comporti ancora come previsto dopo averlo modificato, infondendo così una fiducia duratura e consentendo agli sviluppatori di sentirsi meglio attrezzati per affrontare il lavoro imminente.
È controproducente pensare ai test come a un onere oa qualcosa che rende felici solo i revisori del codice oi lead. I test sono uno strumento di cui noi sviluppatori beneficiamo. Ci piace quando il nostro codice funziona e non ci piace dedicare tempo ad azioni ripetitive o a correggere il codice per risolvere i bug.
Di recente, ho lavorato al refactoring nella mia base di codice e ho chiesto al mio IDE di ripulire le direttive using inutilizzate. Con mia sorpresa, i test hanno mostrato diversi fallimenti nel mio sistema di segnalazione e-mail. Tuttavia, è stato un errore valido: il processo di pulizia ha rimosso alcune direttive using nel codice Razor (HTML + C#) per un modello di posta elettronica e di conseguenza il motore del modello non è stato in grado di creare HTML valido. Non mi aspettavo che un'operazione così piccola avrebbe interrotto i rapporti via e-mail. I test mi hanno aiutato a evitare di passare ore a rilevare bug in tutta l'app subito prima del suo rilascio, quando pensavo che tutto avrebbe funzionato.
Certo, devi saper usare gli strumenti e non tagliarti le proverbiali dita. Potrebbe sembrare che definire il contesto sia noioso e più difficile dell'esecuzione dell'app, che i test richiedano troppa manutenzione per evitare di diventare obsoleti e inutili. Questi sono punti validi e li affronteremo.
2. Selezionare il tipo di test corretto
Gli sviluppatori spesso non apprezzano i test automatizzati perché stanno cercando di deridere una dozzina di dipendenze solo per verificare se vengono chiamate dal codice. In alternativa, gli sviluppatori affrontano un test di alto livello e provano a riprodurre ogni stato dell'applicazione per verificare tutte le variazioni in un piccolo modulo. Questi schemi sono improduttivi e noiosi, ma possiamo evitarli sfruttando diversi tipi di test come previsto. (Dopo tutto, i test dovrebbero essere pratici e divertenti!)
I lettori dovranno sapere cosa sono gli unit test e come scriverli, e avere familiarità con i test di integrazione; in caso contrario, vale la pena fare una pausa qui per aggiornarsi.
Esistono dozzine di tipi di test, ma questi cinque tipi comuni costituiscono una combinazione estremamente efficace:
- Gli unit test vengono utilizzati per testare un modulo isolato chiamando direttamente i suoi metodi. Le dipendenze non vengono testate, quindi vengono derise.
- I test di integrazione vengono utilizzati per testare i sottosistemi. Utilizzi ancora chiamate dirette ai metodi del modulo, ma qui ci preoccupiamo delle dipendenze, quindi non usare dipendenze prese in giro, solo moduli dipendenti (di produzione) reali. È ancora possibile utilizzare un database in memoria o un server Web deriso perché si tratta di derisioni dell'infrastruttura.
- I test funzionali sono test per l'intera applicazione, noti anche come test end-to-end (E2E). Non usi chiamate dirette. Invece, tutta l'interazione passa attraverso l'API o l'interfaccia utente: questi sono i test dal punto di vista dell'utente finale. Tuttavia, l'infrastruttura è ancora presa in giro.
- I test Canary sono simili ai test funzionali ma con un'infrastruttura di produzione e un insieme più piccolo di azioni. Vengono utilizzati per garantire il funzionamento delle applicazioni appena distribuite.
- I test di carico sono simili ai test Canary ma con un'infrastruttura di staging reale e un insieme ancora più piccolo di azioni, che vengono ripetute molte volte.
Non è sempre necessario lavorare con tutti e cinque i tipi di test dall'inizio. Nella maggior parte dei casi, puoi fare molto con i primi tre test.
Esamineremo brevemente i casi d'uso di ogni tipo per aiutarti a selezionare quelli giusti per le tue esigenze.
Test unitari
Richiama l'esempio con prezzi e preferenze di gestione differenti. È un buon candidato per i test unitari perché ci preoccupiamo solo di ciò che sta accadendo all'interno del modulo e i risultati hanno importanti ramificazioni aziendali.
Il modulo ha molte diverse combinazioni di parametri di input e vogliamo ottenere un valore di ritorno valido per ogni combinazione di argomenti validi. Gli unit test sono utili per garantire la validità perché forniscono accesso diretto ai parametri di input della funzione o del metodo e non è necessario scrivere dozzine di metodi di test per coprire ogni combinazione. In molte lingue è possibile evitare la duplicazione dei metodi di test definendo un metodo che accetta gli argomenti necessari per il codice e i risultati previsti. Quindi, puoi utilizzare i tuoi strumenti di test per fornire diversi insiemi di valori e aspettative per quel metodo parametrizzato.
Test di integrazione
I test di integrazione sono adatti ai casi in cui sei interessato a come un modulo interagisce con le sue dipendenze, altri moduli o l'infrastruttura. Utilizzi ancora chiamate dirette al metodo ma non è possibile accedere ai sottomoduli, quindi provare a testare tutti gli scenari per tutti i metodi di input di tutti i sottomoduli non è pratico.
In genere, preferisco avere uno scenario di successo e uno scenario di errore per modulo.
Mi piace utilizzare i test di integrazione per verificare se un contenitore di inserimento delle dipendenze è stato creato correttamente, se una pipeline di elaborazione o calcolo restituisce il risultato previsto o se dati complessi sono stati letti e convertiti correttamente da un database o da un'API di terze parti.
Test funzionali o E2E
Questi test ti danno la massima sicurezza che la tua app funzioni perché verificano che la tua app possa almeno avviarsi senza un errore di runtime. È un po' più di lavoro iniziare a testare il tuo codice senza accedere direttamente alle sue classi, ma una volta compresi e scritti i primi test, scoprirai che non è troppo difficile.
Esegui l'applicazione avviando un processo con argomenti della riga di comando, se necessario, quindi utilizza l'applicazione come farebbe il tuo potenziale cliente: chiamando gli endpoint API o premendo i pulsanti. Questo non è difficile, anche nel caso dei test dell'interfaccia utente: ogni piattaforma principale ha uno strumento per trovare un elemento visivo in un'interfaccia utente.
Test Canarie
I test funzionali ti consentono di sapere se la tua app funziona in un ambiente di test, ma che dire di un ambiente di produzione? Supponiamo che tu stia lavorando con diverse API di terze parti e desideri avere un dashboard dei loro stati o vuoi vedere come la tua applicazione gestisce le richieste in arrivo. Questi sono casi d'uso comuni per i test delle Canarie.
Operano agendo brevemente sul sistema operativo senza causare effetti collaterali a sistemi di terze parti. Ad esempio, puoi registrare un nuovo utente o verificare la disponibilità del prodotto senza effettuare un ordine.
Lo scopo dei test canary è assicurarsi che tutti i componenti principali funzionino insieme in un ambiente di produzione, senza fallire a causa, ad esempio, di problemi con le credenziali.
Test di carico
I test di carico rivelano se l'applicazione continuerà a funzionare quando un numero elevato di persone inizia a utilizzarla. Sono simili ai test canary e funzionali, ma non vengono condotti in ambienti locali o di produzione. Di solito viene utilizzato un ambiente di gestione temporanea speciale, simile all'ambiente di produzione.
È importante notare che questi test non utilizzano servizi di terze parti reali, che potrebbero non essere soddisfatti del test di carico esterno dei loro servizi di produzione e di conseguenza potrebbero addebitare costi aggiuntivi.
3. Tenere separati i tipi di test
Durante l'elaborazione del piano di test automatizzato, ogni tipo di test dovrebbe essere separato in modo da poter essere eseguito in modo indipendente. Sebbene ciò richieda un'organizzazione aggiuntiva, vale la pena perché la combinazione dei test può creare problemi.
Questi test hanno diversi:
- Intenzioni e concetti di base (quindi separarli crea un buon precedente per la prossima persona che guarda il codice, incluso "futuro tu").
- Tempi di esecuzione (quindi eseguire prima gli unit test consente un ciclo di test più rapido quando un test fallisce).
- Dipendenze (quindi è più efficiente caricare solo quelle necessarie all'interno di un tipo di test).
- Infrastrutture richieste.
- Linguaggi di programmazione (in alcuni casi).
- Posizioni nella pipeline di integrazione continua (CI) o al di fuori di essa.
È importante notare che con la maggior parte delle lingue e degli stack tecnologici è possibile raggruppare, ad esempio, tutti gli unit test insieme a sottocartelle denominate in base ai moduli funzionali. Questo è conveniente, riduce l'attrito durante la creazione di nuovi moduli funzionali, è più facile per le build automatizzate, si traduce in meno disordine ed è un altro modo per semplificare i test.
4. Esegui automaticamente i tuoi test
Immagina una situazione in cui hai scritto alcuni test, ma dopo aver ritirato il tuo repository alcune settimane dopo, noti che quei test non stanno più passando.
Questo è uno spiacevole promemoria del fatto che i test sono codice e, come qualsiasi altro pezzo di codice, devono essere mantenuti. Il momento migliore per questo è subito prima del momento in cui pensi di aver finito il tuo lavoro e vuoi vedere se tutto funziona ancora come previsto. Hai tutto il contesto necessario e puoi correggere il codice o modificare i test non riusciti più facilmente rispetto al tuo collega che lavora su un sottosistema diverso. Ma questo momento esiste solo nella tua mente, quindi il modo più comune per eseguire i test è automaticamente dopo un push al ramo di sviluppo o dopo aver creato una richiesta pull.

In questo modo, il tuo ramo principale sarà sempre in uno stato valido, o almeno avrai una chiara indicazione del suo stato. Una pipeline di costruzione e test automatizzata, o una pipeline CI, aiuta a:
- Assicurati che il codice sia compilabile.
- Elimina potenziali problemi "Funziona sulla mia macchina" .
- Fornire istruzioni eseguibili su come preparare un ambiente di sviluppo.
La configurazione di questa pipeline richiede tempo, ma la pipeline può rivelare una serie di problemi prima che raggiungano utenti o clienti, anche quando sei l'unico sviluppatore.
Una volta in esecuzione, CI rivela anche nuovi problemi prima che abbiano la possibilità di aumentare la portata. Pertanto, preferisco configurarlo subito dopo aver scritto il primo test. Puoi ospitare il tuo codice in un repository privato su GitHub e configurare GitHub Actions. Se il tuo repository è pubblico, hai ancora più opzioni rispetto a GitHub Actions. Ad esempio, il mio piano di test automatizzato viene eseguito su AppVeyor, per un progetto con un database e tre tipi di test.
Preferisco strutturare la mia pipeline per i progetti di produzione come segue:
- Compilazione o traspirazione
- Unit test: sono veloci e non richiedono dipendenze
- Configurazione e inizializzazione del database o di altri servizi
- Test di integrazione: hanno dipendenze al di fuori del codice, ma sono più veloci dei test funzionali
- Test funzionali: quando altri passaggi sono stati completati correttamente, esegui l'intera app
Non ci sono canary test o test di carico. A causa delle loro specifiche e requisiti, dovrebbero essere avviati manualmente.
5. Scrivi solo i test necessari
Scrivere unit test per tutto il codice è una strategia comune, ma a volte ciò fa perdere tempo ed energia e non ti dà alcuna sicurezza. Se hai familiarità con il concetto di "piramide di test", potresti pensare che tutto il tuo codice debba essere coperto da unit test, con solo un sottoinsieme coperto da altri test di livello superiore.
Non vedo alcuna necessità di scrivere uno unit test che assicuri che diverse dipendenze derise vengano chiamate nell'ordine desiderato. Ciò richiede l'impostazione di diversi mock e la verifica di tutte le chiamate, ma non mi darebbe comunque la certezza che il modulo funzioni. Di solito, scrivo solo un test di integrazione che utilizza le dipendenze reali e controlla solo il risultato; questo mi dà una certa sicurezza che la pipeline nel modulo testato funzioni correttamente.
In generale, scrivo test che mi semplificano la vita implementando funzionalità e supportandole in un secondo momento.
Per la maggior parte delle applicazioni, puntare a una copertura del codice del 100% aggiunge una grande quantità di lavoro noioso ed elimina la gioia di lavorare con i test e la programmazione in generale. Come afferma la copertura del test di Martin Fowler:
La copertura del test è uno strumento utile per trovare parti non testate di una base di codice. La copertura dei test è di scarsa utilità come affermazione numerica di quanto sono buoni i tuoi test.
Quindi ti consiglio di installare ed eseguire l'analizzatore di copertura dopo aver scritto alcuni test. Il report con le righe di codice evidenziate ti aiuterà a comprendere meglio i suoi percorsi di esecuzione e a trovare i punti scoperti che dovrebbero essere coperti. Inoltre, guardando i tuoi getter, setter e facciate, vedrai perché la copertura del 100% non è divertente.
6. Gioca a Lego
Di tanto in tanto, vedo domande come "Come posso testare metodi privati?" Tu no. Se hai fatto questa domanda, qualcosa è già andato storto. Di solito, significa che hai violato il Principio di responsabilità unica e il tuo modulo non fa qualcosa in modo corretto.
Rifattorizzare questo modulo ed estrarre la logica che ritieni importante in un modulo separato. Non ci sono problemi con l'aumento del numero di file, che porterà al codice strutturato come mattoncini Lego: molto leggibile, manutenibile, sostituibile e testabile.
Una corretta strutturazione del codice è più facile a dirsi che a farsi. Ecco due suggerimenti:
Programmazione Funzionale
Vale la pena conoscere i principi e le idee della programmazione funzionale. La maggior parte dei linguaggi tradizionali, come C, C++, C#, Java, Assembly, JavaScript e Python, ti obbligano a scrivere programmi per macchine. La programmazione funzionale è più adatta al cervello umano.
All'inizio può sembrare controintuitivo, ma considera questo: un computer andrà bene se metti tutto il codice in un unico metodo, usi un blocco di memoria condivisa per memorizzare valori temporanei e usi una discreta quantità di istruzioni di salto. Inoltre, i compilatori nella fase di ottimizzazione a volte lo fanno. Tuttavia, il cervello umano non gestisce facilmente questo approccio.
La programmazione funzionale ti costringe a scrivere funzioni pure senza effetti collaterali, con tipi forti, in modo espressivo. In questo modo è molto più facile ragionare su una funzione perché l'unica cosa che produce è il suo valore di ritorno. L'episodio del podcast Programming Throwdown Programmazione funzionale con Adam Gordon Bell ti aiuterà ad acquisire una comprensione di base e potrai continuare con gli episodi Corecursive God's Programming Language con Philip Wadler e Category Theory con Bartosz Milewski. Gli ultimi due hanno notevolmente arricchito la mia percezione della programmazione.
Sviluppo basato su test
Consiglio di padroneggiare TDD. Il modo migliore per imparare è esercitarsi. String Calculator Kata è un ottimo modo per esercitarsi con il codice kata. Padroneggiare il kata richiederà tempo, ma alla fine ti consentirà di assorbire completamente l'idea di TDD, che ti aiuterà a creare un codice ben strutturato con cui è un piacere lavorare e anche testabile.
Una nota di cautela: a volte vedrai i puristi del TDD affermare che il TDD è l'unico modo corretto di programmare. Secondo me, è semplicemente un altro strumento utile nella tua cassetta degli attrezzi, niente di più.
A volte, è necessario vedere come regolare moduli e processi in relazione tra loro e non sapere quali dati e firme utilizzare. In questi casi, scrivi il codice finché non viene compilato, quindi scrivi i test per risolvere i problemi ed eseguire il debug della funzionalità.
In altri casi, conosci l'input e l'output che desideri, ma non hai idea di come scrivere correttamente l'implementazione a causa della logica complicata. In questi casi, è più facile iniziare a seguire la procedura TDD e costruire il codice passo dopo passo piuttosto che perdere tempo a pensare all'implementazione perfetta.
7. Mantieni i test semplici e mirati
È un piacere lavorare in un ambiente di codice ben organizzato senza inutili distrazioni. Ecco perché è importante applicare i principi SOLID, KISS e DRY ai test, utilizzando il refactoring quando è necessario.
A volte sento commenti del tipo: "Odio lavorare in una base di codice pesantemente testata perché ogni modifica mi richiede di correggere dozzine di test". Questo è un problema di alta manutenzione causato da test che non sono mirati e cercano di testare troppo. Il principio del “Fai bene una cosa” si applica anche ai test: “Testa bene una cosa”; ogni test dovrebbe essere relativamente breve e testare un solo concetto. "Verifica bene una cosa" non significa che dovresti limitarti a un'asserzione per test: puoi usarne dozzine se stai testando mappature di dati non banali e importanti.
Questo focus non è limitato a un test o tipo di test specifico. Immagina di avere a che fare con una logica complicata che hai testato utilizzando unit test, come la mappatura dei dati dal sistema ERP alla tua struttura, e di avere un test di integrazione che accede ad API ERP fittizie e restituisce il risultato. In tal caso, è importante ricordare cosa copre già il tuo unit test in modo da non testare nuovamente la mappatura nei test di integrazione. Di solito è sufficiente assicurarsi che il risultato abbia il campo di identificazione corretto.
Con un codice strutturato come i mattoncini Lego e test mirati, le modifiche alla logica aziendale non dovrebbero essere dolorose. Se le modifiche sono radicali, è sufficiente eliminare il file e i relativi test e fare una nuova implementazione con nuovi test. In caso di modifiche minori, in genere si modificano da uno a tre test per soddisfare i nuovi requisiti e apportare modifiche alla logica. Va bene cambiare i test; puoi pensare a questa pratica come alla contabilità in partita doppia.
Altri modi per ottenere la semplicità includono:
- Elaborazione di convenzioni per la strutturazione dei file di test, la strutturazione del contenuto dei test (tipicamente una struttura Arrange-Act-Assert) e la denominazione dei test; poi, cosa più importante, seguire queste regole in modo coerente.
- Estrazione di blocchi di codice di grandi dimensioni in metodi come "prepara richiesta" e creazione di funzioni di supporto per azioni ripetute.
- Applicazione del modello builder per la configurazione dei dati di test.
- Usando (nei test di integrazione) lo stesso contenitore DI che usi nell'app principale, quindi ogni istanza sarà banale come
TestServices.Get()senza creare dipendenze manualmente. In questo modo sarà facile leggere, mantenere e scrivere nuovi test perché disponi già di utili aiutanti.
Se ritieni che un test stia diventando troppo complicato, fermati e pensa. O il modulo o il test devono essere rifattorizzato.
8. Usa gli strumenti per semplificarti la vita
Durante il test dovrai affrontare molti compiti noiosi. Ad esempio, configurare ambienti di test o oggetti dati, configurare stub e mock per le dipendenze e così via. Fortunatamente, ogni stack tecnologico maturo contiene diversi strumenti per rendere queste attività molto meno noiose.
Ti suggerisco di scrivere i tuoi primi cento test se non l'hai già fatto, quindi investire un po' di tempo per identificare le attività ripetitive e conoscere gli strumenti relativi ai test per il tuo stack tecnologico.
Per trarre ispirazione, ecco alcuni strumenti che puoi utilizzare:
- Corridori di prova. Cerca sintassi concisa e facilità d'uso. Dalla mia esperienza, per .NET, consiglio xUnit (sebbene NUnit sia anche una scelta solida). Per JavaScript o TypeScript, vado con Jest. Cerca di trovare la corrispondenza migliore per i tuoi compiti e la tua mentalità perché gli strumenti e le sfide si evolvono.
- Biblioteche beffarde . Potrebbero esserci mock di basso livello per le dipendenze del codice, come le interfacce, ma ci sono anche mock di livello superiore per API Web o database. Per JavaScript e TypeScript, i mock di basso livello inclusi in Jest sono OK. Per .NET. Uso Moq, anche se NSubstitute è fantastico. Per quanto riguarda i mock delle API web, mi piace usare WireMock.NET. Può essere utilizzato al posto di un'API per la risoluzione dei problemi e il debug della gestione delle risposte. È anche molto affidabile e veloce nei test automatizzati. I database potrebbero essere presi in giro usando le loro controparti in memoria. EfCore in .NET fornisce tale opzione.
- Librerie di generazione dati . Queste utilità riempiono i tuoi oggetti di dati con dati casuali. Sono utili quando, ad esempio, ti interessano solo un paio di campi da un oggetto di trasferimento dati di grandi dimensioni (in tal caso, forse vuoi solo testare la correttezza della mappatura). Puoi usarli per test e anche come dati casuali da visualizzare su un modulo o per riempire il tuo database. A scopo di test, utilizzo AutoFixture in .NET.
- Librerie di automazione dell'interfaccia utente . Si tratta di utenti automatizzati per test automatizzati: possono eseguire la tua app, compilare moduli, fare clic sui pulsanti, leggere etichette e così via. Per navigare attraverso tutti gli elementi della tua app, non devi occuparti del clic per coordinate o del riconoscimento delle immagini; le principali piattaforme dispongono degli strumenti per trovare gli elementi necessari per tipo, identificatore o dati, quindi non è necessario modificare i test ad ogni riprogettazione. Sono robusti, quindi una volta che li hai fatti funzionare per te e CI (a volte scopri che le cose funzionano solo sulla tua macchina ), continueranno a funzionare. Mi piace usare FlaUI per .NET e Cypress per JavaScript e TypeScript.
- Biblioteche di asserzione . La maggior parte dei test runner include strumenti di asserzione, ma ci sono casi in cui uno strumento indipendente può aiutarti a scrivere asserzioni complesse usando una sintassi più chiara e leggibile, come Fluent Assertions per .NET. Mi piace particolarmente la funzione per affermare che le raccolte sono uguali indipendentemente dall'ordine di un articolo o dal suo indirizzo in memoria.
Possa il flusso essere con te
La felicità è strettamente accoppiata con la cosiddetta esperienza di "flusso" descritta in dettaglio nel libro Flow: The Psychology of Optimal Experience . Per ottenere quell'esperienza di flusso, devi essere impegnato in un'attività con una chiara serie di obiettivi ed essere in grado di vedere i tuoi progressi. Le attività dovrebbero comportare un feedback immediato, per il quale i test automatizzati sono l'ideale. Devi anche trovare un equilibrio tra sfide e abilità, che spetta a ogni individuo. I test, in particolare se affrontati con TDD, possono aiutarti a guidarti e infondere fiducia. Ti aiutano a fissare obiettivi specifici, poiché ogni test superato è un indicatore dei tuoi progressi.
Il giusto approccio ai test può renderti più felice e produttivo e i test riducono le possibilità di esaurimento. La chiave è vedere i test come uno strumento (o set di strumenti) che può aiutarti nella routine di sviluppo quotidiana, non come un passaggio gravoso per rendere il tuo codice a prova di futuro.
Il test è una parte necessaria della programmazione che consente agli ingegneri del software di migliorare il modo in cui lavorano, fornire i migliori risultati e utilizzare il loro tempo in modo ottimale. Forse ancora più importante, i test possono aiutare gli sviluppatori a godersi di più il proprio lavoro, aumentando così il morale e la motivazione.
