Testen... Testen... 1, 2, 3: Wie SnapLogic Snaps auf der Apache Spark-Plattform testet

Die SnapLogic Elastic Integration Platform verbindet Ihre Unternehmensdaten, Anwendungen und APIs durch den Aufbau von Drag-and-Drop-Datenpipelines. Jede Pipeline besteht aus Snaps, d. h. intelligenten Konnektoren, die Benutzer auf eine Leinwand ziehen und wie Puzzleteile zusammenstecken können.

Eine SnapLogic-Pipeline wird erstellt und konfiguriert
Eine SnapLogic-Pipeline wird erstellt und konfiguriert

Diese Pipelines werden auf einem Snaplex ausgeführt, einer Anwendung, die auf einer Vielzahl von Plattformen läuft: auf der Infrastruktur eines Kunden, in der SnapLogic-Cloud und neuerdings auch auf Hadoop. Ein Snaplex, der auf Hadoop läuft, kann Pipelines nativ in Spark ausführen.

Die SnapLogic-Datenmanagement-Plattform ist bekannt für ihre benutzerfreundliche Self-Service-Schnittstelle, die durch unser Team engagierter Ingenieure ermöglicht wird(wir stellen ein!). Wir arbeiten daran, die besten Praktiken der Branche anzuwenden, damit unsere Kunden das bestmögliche Endprodukt erhalten - und das Testen ist von grundlegender Bedeutung.

Das Testen ist zwar ein wichtiger Teil des Softwareentwicklungsprozesses, doch ist es oft schwierig, es korrekt durchzuführen. Einige der Herausforderungen, mit denen Entwickler beim Testen von Software konfrontiert werden, sind die Sicherstellung einer angemessenen Testabdeckung sowie die Aktualisierung des Testcodes mit den neuesten Änderungen. Selbst wenn Tests vorhanden und auf dem neuesten Stand sind, ist es möglich, dass die Tests falsch geschrieben sind und den Code nicht richtig bewerten. Ein Witz, der letztes Jahr im Internet die Runde machte, war das folgende Bild mit der Bildunterschrift: "Alle Tests bestehen."

Bild: anonym auf reddit Scherz: wahrscheinlich Keith Smiley (@SmileyKeith) auf Twitter
Bild: anonymous on reddit
Witz: wahrscheinlich Keith Smiley (@SmileyKeith) auf Twitter

Testen hilft, die Qualität von Software zu gewährleisten - aber das hängt von gut geschriebenen Tests ab. Ein schlecht konzipierter Test vermittelt das falsche Vertrauen, dass die Anwendung korrekt funktioniert, verschwendet Zeit und beeinträchtigt die Produktion.

Aus der Sicht eines Entwicklers kann die Möglichkeit, die Implementierung schnell zu iterieren, die Produktivität erheblich steigern. Dies gilt insbesondere, wenn Programme auf einer verteilten Infrastruktur mit großem Overhead ausgeführt werden. Bei der Entwicklung eines Test-Frameworks wollte SnapLogic unseren Ingenieuren die Möglichkeit geben, schnell und sicher einen Snap zu entwickeln und die Testumgebung so zu gestalten, dass sie der Produktionsumgebung entspricht.

Lücken in der Testabdeckung aufspüren: Snaps auf der Apache Spark-Plattform

Momentan werden Snaps für Spark aktiviert, indem eine Methode geschrieben wird, die die Spark RDD-API verwendet, um die Logik des Snaps auszuführen .

.

Im Allgemeinen enthält die RDD-API Operationen, die der Entwickler mit einer Funktion aufruft. Aus diesem Grund wird eine Spark-Implementierung erfordern, dass der Entwickler das RDD vom vorherigen Snap abruft, eine RDD-Operation mit einer Funktion anwendet und dann das RDD in den nächsten Snap schreibt. Der Java-Code für einen Filter-Snap würde zum Beispiel wie folgt aussehen (obwohl dies eine vereinfachte Version der tatsächlichen Implementierung ohne Logik für den Umgang mit der Ausdruckssprache ist):

public void sparkExec(final ExecContext execContext) {
   JavaRDD<Document> javaRDD = execContext.getRDD();
   JavaRDD<Document> filteredRDD = javaRDD.filter(new FilterDocuments(filter));
   execContext.setRDD(filteredRDD);
}

public class FilterDocuments implements Serializable, Function<Document, Boolean> {
    ...

    @Override
    public Boolean call(final Document document) throws Exception {
        return shouldFilter(filter, document);
    }
}

Die Klasse FilterDocuments implementiert die Funktion, die von der sparkExec-Methode verwendet wird, die die Spark RDDs Filter mit dieser Funktion und dem vom vorgelagerten Snap übergebenen RDD aufruft.

Das Testen dieses Codes ist relativ einfach - der eigentliche Kern der Logik ist in der Funktion gekapselt - also sollte das Hinzufügen von Unit-Tests, die die Filterfunktion aufrufen, ausreichen, oder? Wie sich herausstellte, gab es eine ganze Menge Logik innerhalb der sparkExec-Methoden einiger Snaps (allerdings nicht im obigen Beispiel), für die keine automatisierte Testabdeckung implementiert war.

Eine 100%ige Testabdeckung ist in der Theorie schön, kann aber in der Praxis zu Wartungsproblemen führen. Ein möglicher Weg, um die Wartung der Tests zu reduzieren, ist das Weglassen von Code, der zu den Kesseltypen gehört, wie die meisten sparkExec-Methoden. Diese Lücke in unserer Abdeckung wurde durch zusätzliche Tests entdeckt, aber es ist billiger, Fehler früher im Entwicklungszyklus zu finden.

Quelle: IBM Systems Science Research
Quelle: IBM Systems Science Research

Schließung der Lücken

Auf den ersten Blick gibt es für dieses Problem einige mögliche Lösungen:

Mocking des Spark-Frameworks

Eine Möglichkeit wäre, das Spark-Framework zu spiegeln, was eine schnelle Entwicklung und die Automatisierung von Tests ermöglichen würde. Das Mocking von Code in Tests kann jedoch spröde und mühsam zu warten sein. Außerdem würde dies nicht das zweite Ziel erreichen, nämlich die Gewissheit, dass der Code auch in einem verteilten Modus gleich läuft.

Ausführen auf Cluster

Auf der anderen Seite könnte das Framework verlangen, dass der Entwickler Zugang zu einem Spark-Cluster hat, um die Tests auszuführen, was jedoch mit hohen Kosten für Einrichtung und Wartung verbunden wäre und den Entwicklungszyklus verlangsamen würde.

Das Beste aus beiden Welten: Testen im lokalen Spark-Modus

Unsere Lösung wählt einen Mittelweg, der die Vorteile des lokalen Modus von Spark nutzt. Ein Spark-Programm kann so geschrieben werden, dass es auf einem einzelnen Rechner ausgeführt wird, was der Ausführung eines Spark-Programms auf einem Cluster ähnelt, jedoch ohne den Overhead. Dies geschieht durch die Bereitstellung einer Dienstprogramm-Methode, die einen Spark-Kontext zurückgibt, und zwar wie folgt.

public class SparkTestUtil {

   private static final SparkConf SPARK_CONF = new SparkConf()
           .setMaster("local")
           .setAppName("unit test")
           .set("spark.driver.allowMultipleContexts","true");
   private static final JavaSparkContext JAVA_SPARK_CONTEXT = 
           new JavaSparkContext(SPARK_CONF);

   public static JavaSparkContext getSparkContext() {
       return JAVA_SPARK_CONTEXT;
   }
...
}

Dieser Kontext wird von unserem normalen Snap-Test-Framework - das Snaps bereits im normalen Modus konfigurieren und ausführen kann - verwendet, um einen ExecContext zu erstellen. Anschließend kann das Framework die sparkExec-Methode des Snap mit dem ExecContext aufrufen, der auch dem Test des Entwicklers zur Überprüfung der korrekten Ausgabe zugänglich gemacht wird.

Hier ist ein Beispiel für einen Testfall, der dieses Framework mit den Spark-Erweiterungen verwendet:

@TestFixture(snap = CSVParser.class,
       input = "data/csv_parser/test_input_1.json",
       outputs = "out1",
       properties = "data/csv_parser/property_data_1.json",
       runSparkMode = true)
public void testParse(TestResult testResult) throws Exception {
   // Verify Standard mode result
   assertEquals(ImmutableMap.of(
           "First", "Test",
           "Last", "Mike",
           "Phone", "123-456-7890"
   ), testResult.getOutputViews().get(0).getRecordedData());

   // Verify Spark mode result
   ExecContext execContext = testResult.getExecContext();
   assertEquals(ImmutableMap.of(
           "First", "Test",
           "Last", "Mike",
           "Phone", "123-456-7890"
   ), execContext.getRDD("out1").collect().get(0));

Die @TestFixture-Annotation ermöglicht es dem Snap-Entwickler, die Eigenschaften, Eingaben und Ausgaben des Tests zu konfigurieren, und wird verwendet, um Snaps im normalen Modus und im Spark-Modus zu testen. Durch Setzen des runSparkMode auf true gesetzt wird, kann der Test sowohl im normalen als auch im Spark-Modus ausgeführt werden, was die Wiederverwendung von Testfällen ermöglicht.

Letztendlich ermöglicht das Test-Framework den Snap-Entwicklern, ihren Code schneller und effektiver zu erstellen und zu testen, was die Entwicklungszeit bei Snap verkürzt und die Qualität des Produkts erhöht.

 

Um mehr über den Snap-Entwicklungsprozess zu erfahren, besuchen Sie developer.snaplogic.com, wo die notwendigen Schritte zur Entwicklung von Snaps für die SnapLogic Elastic Integration Platform erläutert werden .

Kategorie: Produkt

Wir stellen ein!

Entdecken Sie Ihre nächste große Karrierechance.