La plate-forme d‘intégration élastique SnapLogic relie les données, les applications et les API de votre entreprise en créant des pipelines de données par glisser-déposer. Chaque pipeline est composé de Snaps, qui sont des connecteurs intelligents, que les utilisateurs font glisser sur un canevas et qu‘ils assemblent comme des pièces de puzzle.
Ces pipelines sont exécutés sur un Snaplex, une application qui fonctionne sur une multitude de plateformes : sur l‘infrastructure d‘un client, sur SnapLogic cloud, et plus récemment sur Hadoop. Un Snaplex qui tourne sur Hadoop peut exécuter des pipelines nativement en Spark.
La gestion de données SnapLogic plateforme est connue pour son interface en libre-service facile à utiliser, rendue possible par notre équipe d‘ingénieurs dévoués(nous recrutons !). Nous nous efforçons d‘appliquer les meilleures pratiques du secteur afin que nos clients obtiennent le meilleur produit final possible - et les tests sont fondamentaux.
Bien que les tests constituent une partie importante du processus de développement de logiciels, il est souvent difficile de les réaliser correctement. Certains des défis auxquels les développeurs sont confrontés lorsqu‘ils testent des logiciels consistent à s‘assurer que la couverture des tests est adéquate et à maintenir le code de test à jour en fonction des dernières modifications. Même si les tests existent et sont à jour, il est possible qu‘ils soient mal écrits et qu‘ils n‘évaluent pas le code correctement. Une blague qui a fait le tour de l‘Internet l‘année dernière était l‘image suivante avec la légende : "Tous les tests sont réussis".
Les tests permettent de garantir la qualité des logiciels, mais tout dépend de la qualité de la rédaction des tests. Un test mal conçu donne une fausse assurance que l‘application fonctionne correctement, ce qui fait perdre du temps et a un impact sur la production.
Du point de vue du développeur, la possibilité d‘itérer rapidement sur la mise en œuvre peut grandement améliorer la productivité. Cela est particulièrement vrai lorsque les programmes sont exécutés sur une infrastructure distribuée avec une charge de travail importante. Ainsi, lors du développement d‘un cadre de test, SnapLogic a voulu fournir un moyen pour nos ingénieurs de développer rapidement et en toute confiance un Snap, ainsi que pour l‘environnement de test de ressembler à la façon dont le Snap fonctionnerait en production.
Rattraper les lacunes dans la couverture des tests : Snaps sur Apache Spark plateforme
À l‘heure actuelle, les snaps sont activés par Spark en écrivant une méthode qui utilise l‘API Spark RDD afin d‘exécuter la logique du snap.
En général, l‘API RDD contient des opérations que le développeur appelle avec une fonction. Pour cette raison, une implémentation Spark exigera du développeur qu‘il obtienne le RDD du Snap précédent, qu‘il applique une opération RDD avec une fonction, puis qu‘il écrive le RDD dans le Snap suivant. Par exemple, le code Java pour un Snap de filtre ressemblerait à ceci (bien qu‘il s‘agisse d‘une version simplifiée de l‘implémentation réelle excluant la logique pour traiter le langage d‘expression) :
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); } }
La classe FilterDocuments implémente la fonction utilisée par la méthode sparkExec, qui appelle la fonction filter de Spark avec cette fonction et le RDD transmis par le Snap en amont.
Le test de ce code est relativement simple - la majeure partie de la logique est encapsulée dans la fonction - donc l‘ajout de tests unitaires qui appellent la fonction de filtrage devrait suffire, n‘est-ce pas ? Il s‘est avéré qu‘il y avait une bonne quantité de logique dans les méthodes sparkExec de certains Snaps (mais pas dans l‘exemple ci-dessus), dans laquelle aucune couverture de test automatisée n‘a été mise en œuvre.
Une couverture de test à 100% est une bonne chose en théorie, mais en pratique, cela peut entraîner des problèmes de maintenance. Un moyen potentiel de réduire la maintenance des tests est d‘ignorer les types de code passe-partout, comme la plupart des méthodes sparkExec. Cette lacune dans notre couverture a été découverte par des tests supplémentaires, mais il est moins coûteux de détecter les bogues plus tôt dans le cycle de développement.
Combler les lacunes
À première vue, ce problème a quelques solutions potentielles :
Simuler le cadre de travail Spark
Une option serait d‘imiter le framework Spark, ce qui permettrait de développer rapidement et d‘automatiser les tests. Cependant, l‘imitation du code dans les tests peut être fragile et pénible à maintenir ; de plus, cela n‘atteindrait pas le second objectif qui est de donner confiance que le code fonctionnera de la même manière en mode distribué.
Exécuter sur le cluster
À l‘autre extrême, le cadre pourrait exiger que le développeur ait accès à un cluster Spark afin d‘exécuter les tests, mais cela coûterait cher en termes d‘installation et de maintenance et ralentirait le cycle de développement.
Le meilleur des deux mondes : tester en mode local Spark
Notre solution consiste à trouver un juste milieu en tirant parti du mode local de Spark. Un programme Spark peut être écrit pour s‘exécuter sur une seule machine, ce qui ressemble à la façon dont un programme Spark s‘exécute sur un cluster, mais sans l‘overhead. La façon de procéder est de fournir une méthode utilitaire qui renvoie un contexte Spark, ce qui se fait de la façon suivante.
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; } ... }
Ce contexte est utilisé par notre cadre de test Snap normal - qui peut déjà configurer et exécuter des Snap en mode normal - pour construire un ExecContext. Ensuite, le cadre est capable d‘appeler la méthode sparkExec du Snap avec le ExecContext, qui est également accessible au test du développeur pour vérifier que la sortie est correcte.
Voici un exemple de scénario de test utilisant ce cadre avec les améliorations de Spark :
@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));
L‘annotation @TestFixture permet au développeur du Snap de configurer les propriétés, les entrées et les sorties du test, et est utilisée pour tester les Snaps en mode normal et en mode Spark. En définissant l‘annotation runSparkMode à true cela permettra au test de s‘exécuter à la fois en mode normal et en mode Spark, ce qui permet de réutiliser les cas de test.
En fin de compte, le cadre de test permet aux développeurs de Snap de construire et de tester leur code plus rapidement et plus efficacement, ce qui réduit le temps de développement de Snap et améliore la qualité de leur produit.
Pour en savoir plus sur le processus de développement de Snap, consultez le site developer.snaplogic.com qui explique les étapes nécessaires au développement de Snaps pour la plate-forme d‘intégration élastique SnapLogic.