Detta är del fyra av fem i en handledningsserie om testning av dataintensiv kod med Go. I del tre täckte jag testning mot ett lokalt komplext datalager som innehåller en relationell DB och en Redis-cache.
I denna handledning går jag över testning mot fjärranslutna datalager med hjälp av delade testdatabaser, med hjälp av snapshots av produktionsdata och generering av egna testdata.
Hittills har alla våra test utförts lokalt. Ibland är det inte tillräckligt. Du kan behöva testa mot data som är svåra att generera eller få lokalt. Testdata kan vara väldigt stora eller förändras ofta (exempelvis snapshot av produktionsdata).
I dessa fall kan det vara för långsamt och dyrt för varje utvecklare att kopiera de senaste testdata till sin maskin. Ibland är testdata känsliga, och speciellt fjärrutvecklare borde inte ha den på sin bärbara dator.
Det finns flera alternativ här att överväga. Du kan använda en eller flera av dessa alternativ i olika situationer.
Detta är ett mycket vanligt alternativ. Det finns en gemensam testdatabas som alla utvecklare kan ansluta till och testa mot. Detta delade test DB hanteras som en delad resurs och blir ofta populär med jämna mellanrum med viss baslinjedata och då kan utvecklare köra tester mot det som frågar de befintliga data. De kan också skapa, uppdatera och ta bort egna testdata.
I det här fallet behöver du mycket disciplin och en bra process på plats. Om två utvecklare kör samma test samtidigt som de skapar och raderar samma objekt, kommer båda testen att misslyckas. Observera att även om du är den enda utvecklaren och en av dina test inte städer upp efter sig själv, kan ditt nästa test misslyckas eftersom DB nu har några extra data från det tidigare testet som kan bryta ditt aktuella test.
Så här fungerar CI / CD-pipelines eller till och med bara automatiserade byggsystem. En utvecklare åtar sig en förändring, och en automatiserad bygg- och teststart körs. Men du kan också bara ansluta till en fjärrmaskin som har din kod och kör dina test där.
Fördelen är att du kan kopiera den exakta lokala inställningen, men har tillgång till data som redan finns i fjärrmiljön. Nackdelen är att du inte kan använda dina favoritverktyg för debugging.
Genom att starta en extern ad hoc-testinstans ser du till att du fortfarande är isolerad från andra utvecklare. Det är ganska lika konceptuellt att driva en lokal instans. Du behöver fortfarande starta din datalagring (eller butiker). Du behöver fortfarande fylla dem (fjärr). Din testkod körs dock lokalt, och du kan felsöka och felsöka med din favorit IDE (Gogland i mitt fall). Det kan vara svårt att hantera operativt om utvecklare fortsätter att testa instanser som körs efter testen är färdiga.
När du använder en gemensam testdatabutik är det ofta befolket med produktionsdata snapshots. Beroende på hur känslig och kritisk dataen är kan vissa av följande fördelar och nackdelar vara relevanta.
Fördelar:
Nackdelar:
OK. Du har gjort språnget och bestämt dig för att använda en produktionsdata snapshot. Om dina uppgifter involverar människor i någon form eller form kan du kanske anonymisera data. Detta är överraskande svårt.
Du kan inte bara ersätta alla namn och göra det med det. Det finns många sätt att återställa PII (personligt identifierbar information) och PHI (skyddad hälsoinformation) från dåligt anonymiserade datasnabbbilder. Kolla Wikipedia som utgångspunkt om du är nyfiken.
Jag jobbar för Helix där vi utvecklar en personlig genomicsplattform som behandlar de mest privata data-sekvenserade DNA hos människor. Vi har några seriösa skydd mot oavsiktliga (och skadliga) dataöverträdelser.
När du använder snapshots i produktionsdata måste du periodiskt uppdatera dina ögonblicksbilder och motsvarande dina tester. Tidpunkten är upp till dig, men gör det definitivt när det finns ett schema eller formatändring.
Helst bör dina test inte testa för egenskaper för en viss ögonblicksbild. Om du till exempel uppdaterar dina ögonblicksbilder dagligen och du har ett test som verifierar antalet poster i ögonblicksbilden måste du uppdatera testet varje dag. Det är mycket bättre att skriva dina tester på ett mer generiskt sätt, så du behöver bara uppdatera dem när koden under testet ändras.
Ett annat tillvägagångssätt är att generera egna testdata. Fördelarna och nackdelarna är de exakta motsatserna att använda produktionsdata snapshots. Observera att du också kan kombinera de två metoderna och köra några tester på bilddata och andra tester med hjälp av genererade data.
Hur skulle du gå till att generera dina testdata? Du kan gå vild och använda helt slumpmässiga data. Till exempel, för Songify kan vi bara generera helt slumpmässiga strängar för användarmail, URL, beskrivning och etiketter. Resultatet blir kaotiskt, men giltiga data sedan Songify gör ingen data validering.
Här är en enkel funktion för att generera slumpmässiga strängar:
func makeRandomString (längd int) sträng const bytes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" randBytes: = gör ([] byte, längd) för i: = 0; jag < length; i++ b := bytes[rand.Intn(len(bytes))] randBytes[i] = b return string(randBytes)
Låt oss skriva en funktion som lägger till fem slumpmässiga användare och lägger sedan till 100 slumpmässiga sånger som slumpmässigt fördelas mellan de fem användarna. Vi måste skapa användare eftersom låtar inte lever i vakuum. Varje låt är alltid associerad med minst en användare.
func (m * InMemoryDataLayer) PopulateWithRandomData () users: = [] Användare // Skapa 5 användare för i: = 0; jag < 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 kan vi skriva några tester som driver mycket data. Till exempel, här är ett test som verifierar att vi kan få alla 100 låtar i ett samtal. Observera att testet samtalar PopulateWithRandomData ()
innan samtalet görs.
func TestGetSongs (t * test.T) dl, err: = NewInMemoryDataLayer () om err! = nil t.Error ("Misslyckades med att skapa minnesdata lagret") dl.PopulateWithRandomData () låtar, err: = dl.GetSongs () om err! = nil t.Error ("Misslyckades med att skapa minnesdataskikt") om len (låtar)! = 100 t.Error ('GetSongs () returnerade inte rätt antal låtar ")
Vanligtvis är helt slumpmässiga data inte acceptabla. Varje datalagring har begränsningar som du måste respektera och komplexa relationer som måste följas för att skapa giltiga data som systemet kan fungera på. Du kanske vill skapa några ogiltiga data för att testa hur systemet hanterar det, men det kommer att vara specifika fel du ska injicera.
Tillvägagångssättet kommer att likna den slumpmässiga datagenereringen, förutom att du får mer logik för att tillämpa reglerna.
Låt oss till exempel säga att vi vill genomdriva regeln att en användare kan ha högst 30 låtar. I stället för att slumpmässigt skapa 100 låtar och tilldela dem till användare kan vi bestämma att varje användare kommer att ha exakt 20 låtar, eller kanske skapa en användare utan låtar och fyra andra användare med 25 låtar vardera.
I vissa fall är generering av testdata väldigt komplicerat. Jag har nyligen arbetat med ett projekt som var tvungen att injicera testdata till fyra olika mikrotjänster, var och en som hanterar sin egen databas med data i varje databas relaterad till data i andra databaser. Det var ganska utmanande och arbetsintensivt att hålla allt i synkronisering.
Vanligtvis är det i sådana situationer lättare att använda system API: erna och befintliga verktyg som skapar data istället för att direkt gå in i flera datalager och be att du inte ritar universets tyget. Vi kunde inte ta detta tillvägagångssätt eftersom vi faktiskt behövde skapa några ogiltiga uppgifter avsiktligt för att testa olika felförhållanden och att hoppa över några biverkningar avseende externa system som händer under det normala arbetsflödet.
I den här handledningen täckte vi testning mot fjärrdatabutiker, med hjälp av delade testdatabaser, med hjälp av snapshots av produktionsdata och generering av egna testdata.
I del fem kommer vi att fokusera på fuzz-testning, testa din cache, testa dataintegritet, testa idempotency och sakna data. Håll dig igång.