Dit is deel vier van de vijf in een zelfstudieserie over het testen van gegevensintensieve code met Go. In deel drie behandelde ik het testen met een lokale complexe gegevenslaag met een relationele DB en een Redis-cache.
In deze zelfstudie zal ik het testen tegen externe gegevensopslag testen met behulp van gedeelde testdatabases, momentopnamen van de productiegegevens en het genereren van uw eigen testgegevens.
Tot dusverre zijn al onze tests lokaal uitgevoerd. Soms is dat niet genoeg. Mogelijk moet u testen tegen gegevens die moeilijk lokaal kunnen worden gegenereerd of verkregen. De testgegevens kunnen erg groot zijn of vaak veranderen (bijvoorbeeld snapshot van productiegegevens).
In deze gevallen kan het voor elke ontwikkelaar te langzaam en te duur zijn om de nieuwste testgegevens naar hun machine te kopiëren. Soms zijn de testgegevens gevoelig, en vooral externe ontwikkelaars zouden het niet op hun laptop moeten hebben.
Er zijn verschillende opties om te overwegen. U kunt een of meer van deze opties in verschillende situaties gebruiken.
Dit is een veel voorkomende optie. Er is een gedeelde testdatabase waarmee alle ontwikkelaars verbinding kunnen maken en waarmee ze kunnen testen. Deze gedeelde test-DB wordt beheerd als een gedeelde bron en wordt vaak periodiek gevuld met enkele basisgegevens en vervolgens kunnen ontwikkelaars tests uitvoeren waarmee de bestaande gegevens worden bevraagd. Ze kunnen ook hun eigen testgegevens maken, bijwerken en verwijderen.
In dit geval hebt u veel discipline en een goed proces nodig. Als twee ontwikkelaars dezelfde test uitvoeren op hetzelfde moment dat dezelfde objecten maakt en verwijdert, mislukt beide tests. Houd er rekening mee dat zelfs als u de enige ontwikkelaar bent en een van uw tests zichzelf niet goed opruimt, uw volgende test kan mislukken omdat de DB nu wat extra gegevens van de vorige test heeft die uw huidige test kunnen doorbreken.
Dit is hoe CI / CD-pijpleidingen of zelfs alleen geautomatiseerde build-systemen werken. Een ontwikkelaar maakt een wijziging door en een geautomatiseerde build en test worden gestart. Maar u kunt ook gewoon verbinding maken met een externe machine met uw code en uw tests daar uitvoeren.
Het voordeel is dat u de exacte lokale instellingen kunt repliceren, maar toegang hebt tot gegevens die al beschikbaar zijn in de externe omgeving. Het nadeel is dat je je favoriete tools niet kunt gebruiken voor foutopsporing.
Het starten van een externe ad-hoc testinstantie zorgt ervoor dat u nog steeds geïsoleerd bent van andere ontwikkelaars. Het is conceptueel gezien vergelijkbaar met het uitvoeren van een lokale instantie. U moet uw datastore (of winkels) nog steeds starten. Je moet ze nog steeds invullen (op afstand). Uw testcode wordt echter lokaal uitgevoerd en u kunt fouten opsporen en problemen oplossen met uw favoriete IDE (in mijn geval Gogland). Het kan moeilijk zijn om operationeel te beheren als ontwikkelaars ervoor zorgen dat testexemplaren worden uitgevoerd nadat de tests zijn voltooid.
Bij gebruik van een gedeeld testgegevensarchief worden vaak snapshots van productiegegevens ingevuld. Afhankelijk van hoe gevoelig en kritisch de gegevens zijn, kunnen enkele van de volgende voor- en nadelen relevant zijn.
Voors:
nadelen:
OK. U hebt de sprong gemaakt en besloten om een momentopname van de productiegegevens te gebruiken. Als uw gegevens betrekking hebben op mensen in welke vorm dan ook, moet u de gegevens mogelijk anonimiseren. Dit is verrassend moeilijk.
Je kunt niet zomaar alle namen vervangen en ermee klaar zijn. Er zijn veel manieren om PII (persoonlijk identificeerbare informatie) en PHI (beschermde gezondheidsinformatie) te herstellen van slecht geanonimiseerde momentopnamen van gegevens. Bekijk Wikipedia als een startpunt als je nieuwsgierig bent.
Ik werk voor Helix, waar we een persoonlijk genomics-platform ontwikkelen dat zich bezighoudt met de meest privégegevens - het DNA van mensen dat is gesequenced. We hebben enkele serieuze voorzorgsmaatregelen tegen onbedoelde (en kwaadwillige) datalekken.
Wanneer u snapshots van productiegegevens gebruikt, moet u regelmatig uw snapshots en overeenkomstig uw tests vernieuwen. De timing is aan jou, maar doe het zeker wanneer er een schema of formaatverandering is.
Idealiter zouden uw tests niet moeten testen op eigenschappen van een bepaalde momentopname. Als u bijvoorbeeld uw momentopnames dagelijks vernieuwt en u een test hebt die het aantal records in de momentopname controleert, moet u deze test elke dag bijwerken. Het is veel beter om uw tests op een meer generieke manier te schrijven, dus u hoeft ze alleen te updaten wanneer de code die wordt getest, verandert.
Een andere benadering is het genereren van uw eigen testgegevens. De voors en tegens zijn de exacte tegenpolen van het gebruik van snapshots van productiegegevens. Merk op dat u de twee benaderingen ook kunt combineren en een aantal tests kunt uitvoeren op momentopnamen van productiegegevens en andere tests met behulp van gegenereerde gegevens.
Hoe zou u over het genereren van uw testgegevens gaan? Je kunt wild gaan en totaal willekeurige gegevens gebruiken. Voor Songify kunnen we bijvoorbeeld geheel willekeurige reeksen genereren voor e-mail, URL, beschrijving en labels van de gebruiker. Het resultaat is chaotisch, maar geldige gegevens omdat Songify geen gegevensvalidatie uitvoert.
Hier is een eenvoudige functie voor het genereren van willekeurige tekenreeksen:
func makeRandomString (length int) string const bytes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" randBytes: = make ([] byte, lengte) voor i: = 0; ik < length; i++ b := bytes[rand.Intn(len(bytes))] randBytes[i] = b return string(randBytes)
Laten we een functie schrijven die vijf willekeurige gebruikers toevoegt en vervolgens 100 willekeurige nummers toevoegt die willekeurig tussen de vijf gebruikers worden verdeeld. We moeten gebruikers genereren omdat liedjes niet in een vacuüm leven. Elk nummer is altijd gekoppeld aan ten minste één gebruiker.
func (m * InMemoryDataLayer) PopulateWithRandomData () users: = [] Gebruiker // Maak 5 gebruikers voor i: = 0; ik < 5; i++ name := makeRandomString(15) u := User Email: name + "@" + makeRandomString(12) + ".com", Name: makeRandomString(17), m.CreateUser(u) users = append(users, u) // Create 100 songs and associate randomly with // one of the 5 users for i := 0; i < 100; i++ user := users[rand.Intn(len(users))] song := Song Url: fmt.Sprintf("http://www.%s.com", makeRandomString(13)), Name: makeRandomString(16), m.AddSong(user, song, []Label)
Nu kunnen we een aantal tests schrijven die veel gegevens verwerken. Hier is bijvoorbeeld een test die controleert of we alle 100 nummers in één keer kunnen ontvangen. Merk op dat de testoproepen PopulateWithRandomData ()
voordat u belt.
func TestGetSongs (t * testing.T) dl, err: = NewInMemoryDataLayer () if err! = nil t.Error ("Kan in-memory-gegevenslaag niet maken") dl.PopulateWithRandomData () songs, err: = dl.GetSongs () if err! = nil t.Error ("Kan geen in-memory-gegevenslaag maken") if len (songs)! = 100 t.Error ('GetSongs () heeft niet de juiste gegevens geretourneerd aantal nummers ')
Meestal zijn volledig willekeurige gegevens niet acceptabel. Elke gegevensopslag heeft beperkingen die u moet respecteren en complexe relaties die moeten worden opgevolgd om geldige gegevens te creëren die op het systeem kunnen worden gebruikt. Misschien wilt u ook wat ongeldige gegevens genereren om te testen hoe het systeem omgaat, maar dat zijn specifieke fouten die u gaat injecteren.
De aanpak zal vergelijkbaar zijn met het genereren van willekeurige gegevens, behalve dat u meer logica heeft om de regels af te dwingen.
Laten we bijvoorbeeld zeggen dat we de regel willen afdwingen dat een gebruiker maximaal 30 nummers kan hebben. In plaats van willekeurig 100 nummers te maken en toe te wijzen aan gebruikers, kunnen we besluiten dat elke gebruiker precies 20 nummers heeft, of misschien één gebruiker zonder nummers en vier andere gebruikers met elk 25 nummers maken.
In sommige gevallen is het genereren van testgegevens erg gecompliceerd. Ik heb recent aan een project gewerkt dat testgegevens moest injecteren naar vier verschillende microservices, die elk hun eigen database beheren met de gegevens in elke database die gerelateerd zijn aan de gegevens in andere databases. Het was behoorlijk uitdagend en arbeidsintensief om alles gesynchroniseerd te houden.
Meestal is het in dergelijke situaties gemakkelijker om de systeem-API's en bestaande hulpprogramma's te gebruiken die gegevens maken in plaats van rechtstreeks naar meerdere gegevensopslagruimte te gaan en te bidden dat u de structuur van het universum niet scheurt. We konden deze benadering niet gebruiken, omdat we eigenlijk opzettelijk ongeldige gegevens moesten maken om verschillende foutcondities te testen en om enkele neveneffecten over te slaan met betrekking tot externe systemen die tijdens de normale werkstroom plaatsvinden.
In deze zelfstudie hebben we het testen van gegevensopslag op afstand besproken, gedeelde testdatabases gebruikt, momentopnamen van de productiegegevens en eigen testgegevens gegenereerd.
In deel vijf zullen we ons concentreren op fuzz-testen, het testen van uw cache, het testen van gegevensintegriteit, het testen van idempotency en ontbrekende gegevens. Blijf kijken.