În acest tutorial vă voi învăța toate elementele de bază ale testelor idiomatice în Go folosind cele mai bune practici dezvoltate de designerii de limbă și de comunitate. Arma principală va fi pachetul standard de testare. Obiectivul va fi un program de probă care rezolvă o problemă simplă din Proiectul Euler.
Go este un limbaj de programare incredibil de puternic, învățați totul, de la scrierea de utilități simple la construirea de servere web scalabile și flexibile în întregime.
Problema diferenței pătratului sumar este destul de simplă: "Găsiți diferența dintre suma pătratelor primelor sute de numere naturale și pătratul sumei".
Această problemă particulară poate fi rezolvată destul de concis, mai ales dacă știți Gauss. De exemplu, suma primelor numere N naturale este (1 + N) * N / 2
, și suma pătratelor primelor N întregi este: (1 + N) * (N * 2 + 1) * N / 6
. Deci întreaga problemă poate fi rezolvată prin următoarea formulă și atribuind 100 la N:
(1 + N) * N / 2) * (N + 2)
Ei bine, acest lucru este foarte specific și nu este prea mult de testat. În schimb, am creat câteva funcții care sunt puțin mai generale decât ceea ce este necesar pentru această problemă, dar pot servi și pentru alte programe în viitor (proiectul Euler are 559 de probleme acum).
Codul este disponibil pe GitHub.
Iată semnăturile celor patru funcții:
// Funcția MakeIntList () returnează o serie de numere întregi consecutive // începând de la 1 până la numărul '(inclusiv numărul) func MakeIntList (numărul int) [] int // Funcția squareList () ia o felie din intregi si returneaza o array de quares a acestor intregi func SquareList (numere [] int) [] int // Functia sumList () ia o felie de numere intregi si returneaza suma lor func SumList (numere [] int) int // Rezolvarea Proiectului Euler # 6 - Sumă pătrată difference func Process (number int) int
Acum, cu programul nostru țintă în loc (vă rog să mă ierți, zeiții TDD), să vedem cum să scriem teste pentru acest program.
Pachetul de testare merge mână în mână cu du-te test
comanda. Testarea pachetelor trebuie să se facă în fișiere cu sufixul "_test.go". Puteți să vă împărțiți testele pe mai multe fișiere care respectă această convenție. De exemplu: "whatever1_test.go" și "whatever2_test.go". Ar trebui să puneți funcțiile de testare în aceste fișiere de testare.
Fiecare funcție de testare este o funcție exportată public, a cărei denumire începe cu "Test", acceptă un pointer la a testing.T
obiect, și nu întoarce nimic. Arată ca:
func TestWhatever (t * testing.T) // Codul tau de test merge aici
Obiectul T oferă diferite metode pe care le puteți utiliza pentru a indica erori sau a înregistra erori.
Amintiți-vă că numai funcțiile de testare definite în fișierele de test vor fi executate de către du-te test
comanda.
Fiecare test urmărește același flux: configurați mediul de testare (opțional), introduceți codul sub intrare de testare, capturați rezultatul și comparați-l cu rezultatul așteptat. Rețineți că intrările și rezultatele nu trebuie să fie argumente pentru o funcție.
În cazul în care codul supus încercării este preluarea datelor dintr-o bază de date, atunci intrarea va asigura că baza de date conține datele de testare corespunzătoare (care pot implica batjocorirea la diferite niveluri). Dar, pentru aplicația noastră, scenariul comun de transmitere a argumentelor de intrare către o funcție și de comparare a rezultatului cu ieșirea funcției este suficient.
Să începem cu SumList ()
funcţie. Această funcție ia o felie de numere întregi și returnează suma lor. Aici este o funcție de testare care verifică SumList ()
se comportă așa cum ar trebui.
Acesta testează două cazuri de testare, iar dacă o ieșire așteptată nu se potrivește cu rezultatul, acesta sună la Eroare()
metoda de testare.T obiect.
Funcția TestSumList_NotIdiomatic (t * testing.T) // Test [] -> 0 rezultat: = SumList ([] int ) if result! = 0 t.Error 4, 8, 9 -> 21 rezultat = SumList ([] int 4, 8, 9) dacă rezultatul ! = 21 t.Error ("Pentru intrare:", [] int , "așteptat:", 0, "got:", rezultat)
Acest lucru este simplu, dar pare puțin verbose. Testarea Idiomatic Go utilizează teste de tip table-driven în care definiți un struct pentru perechi de intrări și ieșiri așteptate și apoi aveți o listă a acestor perechi pe care le alimentați într-o buclă la aceeași logică. Iată cum se face pentru testarea SumList ()
funcţie.
tip int int int [] int 1, 2, 3, [] int 12, 13, 25, 7, 57, pentru _, pereche: = teste de domeniu result: = SumList ! = perechea de ieșire t.Error ("Pentru intrare:", pereche.input, "așteptat:", pair.output, "got:", rezultat)
Acest lucru este mult mai bun. Este ușor să adăugați mai multe cazuri de testare. Este ușor să aveți întregul spectru de cazuri de testare într-un singur loc și dacă decideți să modificați logica de testare, nu este necesar să modificați mai multe instanțe.
Iată un alt exemplu pentru testarea SquareList ()
funcţie. În acest caz, atât intrarea, cât și ieșirea sunt felii de întregi, astfel încât structura de perechi struct este diferită, dar debitul este identic. Un lucru interesant este că Go nu oferă o metodă integrată pentru a compara felii, așa că folosesc reflect.DeepEqual ()
pentru a compara felia de ieșire cu felia așteptată.
tip [] [int] int int , int 3, 5, int 9, 25, 49, pentru _, pereche: = testele intervalului result: = SquareList (pair.input) dacă! reflect.DeepEqual (rezultatul, perechea.output) t.Error ("Pentru input:", pair.input, , pair.output, "got:", rezultat)
Running teste este la fel de simplu ca tastarea du-te test
în directorul de pachete. Go va găsi toate fișierele cu sufixul "_test.go" și toate funcțiile cu prefixul "Test" și le executați ca teste. Iată cum arată atunci când totul este OK:
(G) / project-euler / 6 / go> go test PASS ok _ / Utilizatori / gigi / Documents / dev / github / project-euler / 6 / go 0.006s
Nu foarte dramatic. Lasă-mă să fac un test cu intenție. Voi schimba cazul de testare SumList ()
astfel încât rezultatul așteptat pentru sumele 1 și 2 va fi de 7.
Functia TestSumList (t * test.T) var teste = [] 2, int 1, 2, 7 , int 12, 13, 25, 7, 57, pentru _, pereche: = teste de interval result: = SumList (pair.input) Pentru intrare: ", pair.input," expected: ", pair.output," got: ", rezultat)
Acum, când tastați du-te test
, primesti:
(0) 006_sum_square_difference_test.go: 80: Pentru intrare: [1 2] așteptat: 7 primit: 3 Stare de ieșire FAIL 1 FAIL _ / / Utilizatori / gigi / Documents / dev / github / project-euler / 6 / merge 0.006s
Aceasta afirmă destul de bine ce sa întâmplat și ar trebui să vă dea toate informațiile de care aveți nevoie pentru a rezolva problema. În acest caz, problema este că testul în sine este greșit și valoarea așteptată ar trebui să fie 3. Aceasta este o lecție importantă. Nu presupune automat că dacă un test nu reușește codul testat este rupt. Luați în considerare întregul sistem, care include codul testat, testul în sine și mediul de testare.
Pentru a vă asigura că codul dvs. funcționează, nu este suficient să aveți teste care să treacă. Un alt aspect important este acoperirea testului. Testele dvs. acoperă fiecare declarație din cod? Uneori chiar nu este suficient. De exemplu, dacă aveți o buclă în codul dvs. care rulează până când o condiție este îndeplinită, o puteți testa cu o condiție care funcționează, dar nu observați că, în unele cazuri, condiția poate fi întotdeauna falsă, rezultând o buclă infinită.
Testele unice sunt ca și cum ți-ai periajul din dinți și dentare. Nu trebuie să le neglijezi. Ele reprezintă prima barieră împotriva problemelor și vă vor permite să aveți încredere în refactorizare. Ele sunt, de asemenea, o binefacere atunci când încearcă să reproducă problemele și să poată scrie un test care demonstrează problema care trece după ce ați rezolvat problema.
Sunt necesare teste de integrare. Gândiți-vă la ele ca la medicul dentist. Poate ești în regulă fără ei pentru un timp, dar dacă le neglijezi prea mult, nu va fi frumos.
Cele mai multe programe non-triviale sunt realizate din mai multe module sau componente inter-conexe. Problemele pot apărea adesea la conectarea împreună a acestor componente. Testele de integrare vă dau încredere că întregul dvs. sistem funcționează conform destinației. Există multe alte tipuri de teste, cum ar fi testele de acceptare, testele de performanță, testele de sarcină / sarcină și testele complete de sistem, dar testele unitare și testele de integrare reprezintă două dintre modalitățile fundamentale de testare a software-ului.
Go are suport încorporat pentru testare, o metodă bine definită de scriere a testelor și recomandări recomandate sub formă de teste efectuate pe masă.
Nevoia de a scrie structuri speciale pentru fiecare combinație de intrări și ieșiri este un pic enervant, dar acesta este prețul pe care îl plătiți pentru abordarea Go simplu de proiectare.