Testen des datenintensiven Codes mit Go, Teil 4

Überblick

Dies ist Teil vier von fünf einer Tutorialserie zum Testen von datenintensivem Code mit Go. In Teil drei habe ich Tests mit einer lokalen komplexen Datenschicht durchgeführt, die eine relationale Datenbank und einen Redis-Cache umfasst.

In diesem Lernprogramm werde ich die Remote-Datenspeicher mit freigegebenen Testdatenbanken testen, Produktionsdaten-Snapshots verwenden und eigene Testdaten generieren.

Testen gegen Remote-Datenspeicher

Bisher wurden alle unsere Tests vor Ort durchgeführt. Manchmal reicht das nicht. Möglicherweise müssen Sie Daten testen, die schwer zu generieren oder lokal zu erhalten sind. Die Testdaten können sehr groß sein oder sich häufig ändern (z. B. Produktionsdaten-Snapshot)..

In diesen Fällen kann es für jeden Entwickler zu langsam und zu teuer sein, die neuesten Testdaten auf den Computer zu kopieren. Manchmal sind die Testdaten vertraulich, und insbesondere Remote-Entwickler sollten sie nicht auf ihrem Laptop haben.

Hier gibt es mehrere Optionen, die zu berücksichtigen sind. Sie können eine oder mehrere dieser Optionen in verschiedenen Situationen verwenden.

Freigegebene Testdatenbank

Dies ist eine sehr verbreitete Option. Es gibt eine gemeinsam genutzte Testdatenbank, mit der sich alle Entwickler verbinden und testen können. Diese gemeinsam genutzte Test-Datenbank wird als gemeinsam genutzte Ressource verwaltet und wird häufig regelmäßig mit einigen Basisdaten gefüllt. Anschließend können Entwickler Tests ausführen, um die vorhandenen Daten abzufragen. Sie können auch ihre eigenen Testdaten erstellen, aktualisieren und löschen.

In diesem Fall brauchen Sie viel Disziplin und einen guten Prozess. Wenn zwei Entwickler denselben Test gleichzeitig ausführen und dieselben Objekte erstellen und löschen, schlagen beide Tests fehl. Beachten Sie, dass der nächste Test möglicherweise fehlschlägt, auch wenn Sie der einzige Entwickler sind und einer Ihrer Tests nicht ordnungsgemäß bereinigt wird, da die Datenbank jetzt einige zusätzliche Daten aus dem vorherigen Test enthält, die Ihren aktuellen Test unterbrechen können. 

Tests remote ausführen

So funktionieren CI / CD-Pipelines oder auch nur automatisierte Build-Systeme. Ein Entwickler übernimmt eine Änderung und ein automatischer Build- und Teststart wird ausgeführt. Sie können aber auch einfach eine Verbindung zu einem Remote-Computer herstellen, auf dem sich Ihr Code befindet, und dort Ihre Tests ausführen.

Der Vorteil ist, dass Sie das genaue lokale Setup replizieren können, aber Zugriff auf Daten haben, die bereits in der Remote-Umgebung verfügbar sind. Der Nachteil ist, dass Sie Ihre Lieblingstools nicht zum Debuggen verwenden können.

Ad-hoc-Remote-Testinstanz

Durch das Starten einer Remote-Ad-hoc-Testinstanz wird sichergestellt, dass Sie weiterhin von anderen Entwicklern isoliert sind. Es ist konzeptionell ziemlich ähnlich wie das Ausführen einer lokalen Instanz. Sie müssen weiterhin Ihren Datenspeicher (oder die Datenspeicher) starten. Sie müssen sie immer noch aus der Ferne auffüllen. Ihr Testcode wird jedoch lokal ausgeführt, und Sie können mit Ihrer bevorzugten IDE (in meinem Fall Gogland) debuggen und Fehler beheben. Die operative Verwaltung kann schwierig sein, wenn die Testinstanzen nach den Tests weiterhin ausgeführt werden.

Verwenden von Produktionsdaten-Snapshots

Wenn Sie einen gemeinsam genutzten Testdatenspeicher verwenden, wird dieser häufig mit Produktionsdaten-Snapshots gefüllt. Abhängig davon, wie vertraulich und kritisch die Daten sind, können einige der folgenden Vor- und Nachteile relevant sein.

Vor- und Nachteile der Verwendung von Produktionsdaten zum Testen

Pros:

  • Sie testen mit realen Daten. Wenn es funktioniert, bist du gut.
  • Sie können Lade- und Leistungstestdaten laden, die eine tatsächliche Last darstellen.
  • Sie müssen keine Datengeneratoren schreiben, die versuchen, echte Produktionsdaten zu simulieren.

Nachteile:

  • Es ist möglicherweise nicht leicht, Fehlerbedingungen zu testen.
  • Produktionsdaten können empfindlich sein und eine besondere Behandlung erfordern.
  • Sie müssen Code schreiben oder Ihren Snapshot regelmäßig manuell synchronisieren.
  • Sie müssen sich mit Format- oder Schemaänderungen befassen.
  • Es kann schwierig sein, Probleme zu isolieren, die mit unordentlichen Produktionsdaten verbunden sind.

Produktionsdaten anonymisieren

OK. Sie haben den Sprung gemacht und beschlossen, einen Produktionsdaten-Snapshot zu verwenden. Wenn Ihre Daten in irgendeiner Form Menschen enthalten, müssen Sie die Daten möglicherweise anonymisieren. Das ist überraschend schwierig.

Sie können nicht einfach alle Namen ersetzen und damit fertig sein. Es gibt viele Möglichkeiten, personenbezogene Daten (persönlich identifizierbare Informationen) und PHI (geschützte Gesundheitsinformationen) aus schlecht anonymisierten Daten-Snapshots wiederherzustellen. Schauen Sie sich Wikipedia als Ausgangspunkt an, wenn Sie neugierig sind.

Ich arbeite für Helix, wo wir eine persönliche Genomik-Plattform entwickeln, die sich mit den privatesten Daten - der sequenzierten DNA von Menschen - befasst. Wir haben ernsthafte Sicherheitsvorkehrungen gegen versehentliche (und böswillige) Datenverstöße.

Aktualisieren von Tests und Daten-Snapshots

Bei der Verwendung von Produktionsdaten-Snapshots müssen Sie Ihre Snapshots und Ihre Tests entsprechend regelmäßig aktualisieren. Das Timing liegt bei Ihnen, aber tun Sie es auf jeden Fall, wenn ein Schema oder ein Formatwechsel vorliegt. 

Idealerweise sollten Ihre Tests nicht die Eigenschaften eines bestimmten Schnappschusses testen. Wenn Sie beispielsweise Ihre Snapshots täglich aktualisieren und Sie einen Test haben, mit dem die Anzahl der Datensätze im Snapshot überprüft wird, müssen Sie diesen Test täglich aktualisieren. Es ist viel besser, Ihre Tests allgemeiner zu schreiben. Sie müssen sie also nur aktualisieren, wenn sich der getestete Code ändert. 

Testdaten generieren

Ein anderer Ansatz ist das Generieren eigener Testdaten. Das Für und Wider ist das genaue Gegenteil der Verwendung von Produktionsdaten-Snapshots. Beachten Sie, dass Sie die beiden Ansätze auch kombinieren und einige Tests mit Produktionsdaten-Snapshots und anderen Tests unter Verwendung der generierten Daten durchführen können.

Zufällige Testdatengenerierung

Wie würden Sie Ihre Testdaten generieren? Sie können wild gehen und völlig zufällige Daten verwenden. Für Songify können wir beispielsweise völlig zufällige Zeichenfolgen für E-Mail-Adresse, URL, Beschreibung und Labels von Benutzern generieren. Das Ergebnis ist chaotisch, aber gültige Daten, da Songify keine Datenvalidierung durchführt.

Hier ist eine einfache Funktion zum Erzeugen von zufälligen Zeichenfolgen:

func makeRandomString (length int) Zeichenfolge const bytes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" randBytes: = make ([] byte, length) für i: = 0; ich < length; i++  b := bytes[rand.Intn(len(bytes))] randBytes[i] = b  return string(randBytes) 

Lassen Sie uns eine Funktion schreiben, die fünf zufällige Benutzer hinzufügt und dann 100 zufällig ausgewählte Songs zwischen den fünf Benutzern hinzufügt. Wir müssen Benutzer generieren, weil Songs nicht in einem Vakuum leben. Jedem Song ist immer mindestens ein Benutzer zugeordnet.

func (m * InMemoryDataLayer) PopulateWithRandomData () Benutzer: = [] Benutzer  // Erstellen Sie 5 Benutzer für i: = 0; ich < 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)   

Nun können wir einige Tests schreiben, die viele Daten verarbeiten. Hier ist zum Beispiel ein Test, der bestätigt, dass wir alle 100 Songs in einem Anruf erhalten können. Beachten Sie, dass der Test aufruft PopulateWithRandomData () bevor Sie den Anruf tätigen. 

func TestGetSongs (t * testing.T) dl, err: = NewInMemoryDataLayer () if err! = nil t.Error ("Fehler beim Erstellen der In-Memory-Datenschicht") dl.PopulateWithRandomData () songs, err: = dl.GetSongs () if err! = nil t.Error ("Fehler beim Erstellen der In-Memory-Datenschicht"), wenn len (Songs)! = 100 t.Error ('GetSongs () hat nicht die korrekten Werte zurückgegeben Anzahl der Lieder ') 

Regelbasierte Testdatengenerierung

Normalerweise sind völlig zufällige Daten nicht akzeptabel. Jeder Datenspeicher hat Einschränkungen, die Sie beachten müssen, und komplexe Beziehungen, die eingehalten werden müssen, um gültige Daten zu erstellen, mit denen das System arbeiten kann. Möglicherweise möchten Sie auch einige ungültige Daten generieren, um zu testen, wie das System damit umgeht. Es handelt sich dabei jedoch um spezifische Fehler, die Sie einfügen.

Der Ansatz ähnelt der Zufallsdatengenerierung, mit der Ausnahme, dass Sie mehr Logik haben, um die Regeln durchzusetzen. 

Nehmen wir zum Beispiel an, wir möchten die Regel durchsetzen, dass ein Benutzer höchstens 30 Songs haben kann. Anstatt 100 Songs zufällig zu erstellen und sie Benutzern zuzuweisen, können wir entscheiden, dass jeder Benutzer genau 20 Songs hat, oder vielleicht einen Benutzer ohne Songs und vier andere Benutzer mit jeweils 25 Songs. 

Narrativ-basierte Testdatengenerierung

In einigen Fällen ist das Erzeugen von Testdaten sehr kompliziert. Ich habe kürzlich an einem Projekt gearbeitet, bei dem Testdaten in vier verschiedene Mikrodienste eingegeben werden mussten, von denen jeder seine eigene Datenbank mit den Daten in jeder Datenbank verwaltet, die sich auf die Daten in anderen Datenbanken beziehen. Es war ziemlich herausfordernd und arbeitsintensiv, alles synchron zu halten.

In solchen Situationen ist es in der Regel einfacher, die System-APIs und vorhandenen Tools zu verwenden, mit denen Daten erstellt werden, anstatt direkt in mehrere Datenspeicher zu gehen und zu beten, dass Sie die Struktur des Universums nicht zerreißen. Wir konnten diesen Ansatz nicht wählen, weil wir tatsächlich absichtlich ungültige Daten erstellen mussten, um verschiedene Fehlerbedingungen zu testen und einige Nebeneffekte in Bezug auf externe Systeme zu überspringen, die während des normalen Arbeitsablaufs auftreten. 

Fazit

In diesem Lernprogramm haben wir Tests mit Remote-Datenspeichern, gemeinsame Testdatenbanken, Produktionsdaten-Snapshots und das Generieren eigener Testdaten beschrieben.

Im fünften Teil konzentrieren wir uns auf das Testen von Fuzz, das Testen des Caches, das Testen der Datenintegrität, das Testen der Idempotenz und auf fehlende Daten. Bleib dran.