Analytics

Rédaction de tests pour des modèles personnalisés dans Google Tag Manager

Google Tag Manager a introduit la possibilité d’ajouter des tests à vos modèles personnalisés. Les tests, dans ce contexte, se réfèrent spécifiquement à tests unitaires que vous écrivez afin de vous assurer que le code de votre modèle fonctionne de manière prévisible. Les tests unitaires sont également utilisés pour piloter le développement, en veillant à ce que vous ayez ajouté des contingences pour tous les différents scénarios que le modèle, lorsqu’il est associé à une entrée utilisateur, pourrait introduire.

Tests de modèles personnalisés

Dans ce guide, je vais vous présenter comment le Essais fonctionnalité fonctionne. De nouvelles API sont introduites, ainsi qu’un grammaire du genre auquel vous pourriez être habitué si vous avez déjà travaillé avec des tests dans un contexte de développement logiciel.

Table des matières

Table des matières

[+show] [–hide]

Tests unitaires dans les modèles personnalisés

Tests unitaires comprennent du code qui est exécuté sur les fonctions et variables réelles de votre code de modèle. Le but du test est de valider le code du modèle, de sorte qu’il renvoie une sortie attendue pour toute entrée testée donnée.

Le but des tests unitaires est de rendre votre code plus résistant aux changements. En écrivant des tests, vous disposez d’un outil pour vérifier que les modifications que vous apportez à un endroit ne cassent pas quelque chose qui se passe à un autre endroit.

Avec les tests unitaires, vous testez les composants les plus atomiques de votre code : les fonctions et les variables. Lorsque vous écrivez des tests, vous visez des résultats élevés couverture de code. La couverture signifie le nombre de lignes de code qui sont évalués dans certains tests.

Exemple simple de couverture

Par exemple, supposons que vous ayez un modèle de variable simple comme celui-ci :

const getType = require('getType'); const input = data.input; if (getType(input) === 'array') {   return; } return input; 

Ce modèle prend l’entrée de l’utilisateur et la renvoie. Si l’entrée est de type déployerensuite undefined est retourné à la place.

Lors de l’écriture de tests pour cette variable, il y a essentiellement deux scénarios que vous auriez besoin de tester.

1. La variable renvoie-t-elle undefined pour l’entrée de tableau

// Call runCode to run the template's code. const mockData = {   input: 'notAnArray' };  const variableResult = runCode(mockData);  // Verify that the variable returns a result. assertThat(variableResult).isUndefined(); 

Ici, vous créez un faux objet qui fournit l’entrée. Cet objet a des paires clé-valeur où chaque clé correspond à une propriété dans le data objet, reflétant l’entrée que l’utilisateur aurait ajoutée aux champs du modèle.

Cet objet est passé au runCode fonction, qui est un modèle d’API qui exécute le code du modèle sur l’objet de données.

Enfin, le résultat de runCode est passé à assertThatqui est un affirmation méthode où vous vérifiez si le résultat du code se résout en une valeur attendue.

2. La variable renvoie-t-elle l’entrée

const mockInput = 'something'; const mockData = {   input: mockInput };  const expected = mockInput;  const variableResult = runCode(mockData);  // Verify that the variable returns a result. assertThat(variableResult).isEqualTo(expected); 

Comme vous pouvez le voir, j’utilise des constantes et des variables dans la mesure du possible pour éviter de coder en dur des valeurs. Cela évite que de simples fautes de frappe créent des faux positifs ou négatifs dans vos tests.

Avec ces deux tests, vous obtenez réellement Couverture des tests à 100 % pour le modèle de variable, car le code teste toutes les variations possibles de la variable.

Qu’en est-il de l’entrée manquante ?

Lors de la rédaction de tests, vous êtes également confronté à des décisions de conception intéressantes.

Prenons l’exemple ci-dessus. Nous ne testons pas spécifiquement un scénario où l’utilisateur n’a pas ajouté quelconque entrée sur le terrain. Pourquoi? Parce que nous avons décidé de gérer cela dans l’interface utilisateur, en ajoutant une règle de validation qui empêche le code du modèle de s’exécuter s’il n’y a pas d’entrée dans le champ.

Ainsi, lors de l’écriture de tests, vous devez savoir comment fonctionne le modèle et vous devez être en mesure d’aborder de manière exhaustive les différents scénarios que les utilisateurs imaginatifs peuvent exploiter lorsqu’ils travaillent avec le code de votre modèle.

L’anatomie d’un test

Vous avez appris ci-dessus que les tests unitaires ont des composants spécifiques. Ce sont, dans l’ordre :

Maquette de l’objet de données
Simulations des modèles d’API (mock())
Le testeur (runCode())
(Facultatif fail())
Assertions (assertApi(), assertThat())

1. Moquerie

Pour tester une fonction, vous utilisez fausses données. En effet, avec les tests automatisés, il n’y a pas d’invite pour la saisie de l’utilisateur. Le test doit être exécuté indépendamment du modèle lui-même. Avec les tests unitaires, vous êtes seul tester le code lui-même, pas l’expérience utilisateur.

Dans les modèles personnalisés de Google Tag Manager, les données fictives se présentent sous deux formes : le contenu du data objet et le résultat de l’exécution des modèles d’API.

Se moquer de la data chose

Le premier est simple à faire. Vous construisez un objet, où chaque clé correspond à une propriété du data objet que votre code traite.

Par exemple, si votre modèle comporte un champ de saisie de texte nommé name puis un simple champ de table nommé optionalParametersvous pouvez créer un objet fictif comme celui-ci :

const mockData = {   name: 'Simo Ahava',   optionalParameters: [{     parameterName: 'country',     parameterValue: 'Finland'   },{     parameterName: 'hair_color',     parameterValue: 'n/a'   }] }; 

Cet objet de données factice correspondrait à une entrée de champ comme celle-ci :

Données factices

Vous transmettez cet objet de données fictif au runCode (plus d’informations ci-dessous) afin que le code du modèle soit exécuté avec l’entrée de l’objet.

Modèles d’API moqués

Lorsque vous exécutez des tests pour votre modèle, vous ne souhaitez peut-être pas que le modèle effectue réellement les appels d’API pour lesquels il est configuré.

Appel sendPixel maintes et maintes fois avec différentes entrées pourraient ne pas faire beaucoup de sens.

C’est là que la moquerie d’API joue un rôle essentiel. Dans l’interface Tests, vous pouvez remplacer Fonctionnalité API avec une fonction fictive. De cette façon, vous pouvez exécuter le test comme si le modèle avait appelé l’API et traité la valeur renvoyée comme si l’API l’avait renvoyé.

Par exemple, supposons que nous ayons un modèle de balise qui injecte un script sur la page, où le nom de domaine est entré par l’utilisateur dans un champ de modèle. Voici à quoi ressemblerait le code du modèle :

const sendPixel = require('sendPixel');  const domainName = data.domainName;  sendPixel(domainName, data.gtmOnSuccess, data.gtmOnFailure); 

Pour tester cela, vous pouvez écrire un test qui vérifie simplement si gtmOnSuccess a été appelé pour un nom d’hôte valide et gtmOnFailure pour un invalide :

const mockData = {   domainName: 'https://invalid.com' }; runCode(mockData); assertApi('gtmOnFailure').wasCalled(); 

Cependant, ce test essaie en fait d’envoyer la requête de pixel au nom de domaine, ce qui signifie qu’il est difficile de tester des résultats positifs et négatifs sans polluer le point de terminaison.

Au lieu de cela, vous pouvez faux l’API comme ceci :

const mockData = {   domainName: 'https://invalid.com' };  mock('sendPixel', (url, onSuccess, onFailure) => {   onFailure(); });  runCode(mockData);  assertApi('gtmOnFailure').wasCalled(); 

Avec mockvous demandez au test d’utiliser le stub de fonction que vous fournissez, c’est-à-dire chaque fois que le sendPixel API est appelée, elle exécute toujours le onFailure() méthode indépendamment de l’entrée (au moins, dans ce test actuel).

Ainsi, lorsque vous utilisez des API qui invoquent des points de terminaison externes, il peut être judicieux de les simuler.

Les simulations sont réinitialisées à chaque test, vous n’avez donc pas à vous soucier de votre onFailure() fuite dans d’autres tests, aussi.

2. Exécuter le code

Une fois vos simulations prêtes, vous pouvez invoquer le runCode() méthode.

Cette méthode exécute le code du modèle, en se moquant des API que vous avez choisi de remplacer et en utilisant l’objet de données que vous lui transmettez comme argument.

Si vous ne lui transmettez aucun objet en tant qu’argument, ou si vous transmettez un objet vide, le modèle sera exécuté comme si l’utilisateur n’avait ajouté aucune entrée à aucun champ.

Avec modèles de balisesil vous suffit d’exécuter runCode(), car vos affirmations seront faites par rapport aux API qui ont été appelées. Les modèles de balises ne renvoient rien, il n’y a donc rien à évaluer à partir du résultat de runCode().

Avec modèles variablesle modèle devrait en fait revenir quelque chose, et donc vos affirmations doivent également être faites contre le résultat de runCode().

const mockData = {input: 'test'};  // With tag templates runCode(mockData); assertApi('someApi').wasCalled();  // With variable templates const result = runCode(mockData); assertThat(result).isEqualTo('test'); 

Une fois le test exécuté, vous êtes prêt à affirmer les résultats.

3. Échec du test

Les modèles personnalisés offrent une API que vous pouvez utiliser pour échouer immédiatement à un test.

Notez que le cas d’utilisation typique de fail() est d’évaluer la se tester plutôt que le code qui est testé.

Certains aiment utiliser un mécanisme d’échec pour indiquer des tests qui n’ont pas encore été écrits, d’autres aiment l’utiliser pour exposer des chemins qui ne devraient jamais être rencontrés par des tests.

En règle générale, vous n’utiliseriez pas fail() dans le code prêt pour la production, car il n’y aurait pas de tests incomplets et la logique d’assertion gérerait toutes les permutations possibles du code du modèle.

Utiliser fail() est très simple – ajoutez simplement la commande à une branche du code et définissez le message d’échec comme argument.

const mockData = {input: 'test'};  const result = runCode(mockData);  if (result === undefined) {   fail('Result should never be undefined.'); } assertThat(result).isEqualTo('test'); 

Vous pouvez utiliser fail() pour créer des assertions personnalisées. Par exemple, pour vérifier si l’objet renvoyé par le code contient une propriété, vous pouvez faire ceci :

const mockData = {input: 'test'};  const result = runCode(mockData);  if (result.hasOwnProperty('someProperty')) {   assertThat(true); } else {   fail('Variable did not have "someProperty".'); } 

Ce n’est pas joli, mais il fait son travail en attendant l’ajout de nouvelles API d’assertion.

4. Assertions

Avec affirmations, vous faites des déclarations sur les résultats du test. Les meilleures bibliothèques d’assertions sont un plaisir à utiliser, car les assertions sont écrites avec une grammaire et une syntaxe qui rappellent le langage réel.

Par exemple, pour affirmer que le code du modèle de variable renvoie une valeur spécifique, vous ajoutez quelque chose comme :

assertThat(runCodeResult).isEqualTo('some value'); 

L’épreuve est une Succès si l’assertion se résout à true. Autrement dit, si le runCodeResult avait exactement la valeur 'some value'le test passerait.

Avec les API, vous affirmez si l’API a été appelée ou non.

assertApi('sendPixel').wasCalled(); 

Ce test serait un succès si le sendPIxel L’API a été invoquée lors de l’exécution du code de test.

REMARQUE! Pour tester si une API a été appelée avec des paramètres spécifiques, vous devez faux cette API à la place.

Vous pouvez ajouter plus d’une affirmation à un cas de test. Toutes les assertions doivent réussir pour que le test soit un succès. Je recommande cependant d’essayer de limiter au minimum le nombre d’assertions par test, car le fait d’avoir des cas de test granulaires vous permet d’identifier plus facilement le lien entre le test qui a échoué et le code cassé qui l’a fait échouer.

En utilisant assertThat

Les assertThat L’API vous permet de faire des assertions sur le résultat de l’exécution du code. Les assertions suivent la syntaxe utilisée par exemple par Google’s Truth et la bibliothèque AssertJ.

Les assertThat L’API est particulièrement utile lors du test de modèles de variables, car les assertions seraient exécutées sur le résultat (la valeur renvoyée) du modèle de variable lui-même.

La syntaxe est celle-ci :

assertThat(someValueToTest).assertion(someExpectedValue);

Les assertThat() La méthode prend la valeur à tester comme argument et renvoie un objet avec toutes les différentes API d’assertion répertoriées ci-dessous.

Certaines des API (par exemple isEqualTo()) prennent un argument, où la valeur affirmée est testée par rapport à cet argument.

De nombreuses API ne prennent aucun argument – elles sont utilisées pour des assertions simples et rapides, par exemple si la valeur affirmée a été définie ou non.

Voici les assertions prises en charge par l’API.

Affirmation La description
isEqualTo(someValue) Vérifie si la valeur affirmée est égale à someValue. L’égalité n’est pas stricte – vous pouvez vérifier si [1, 2, 3] équivaut à [1, 2, 3] et le test passera. De même, vous pouvez vérifier des objets complexes et l’assertion réussira s’ils ont exactement les mêmes clés et valeurs.
isNotEqualTo(someValue) L’assertion est un succès si la valeur affirmée n’est pas égale à someValue. Les mêmes règles de rigueur s’appliquent ici.
isStrictlyEqualTo(someValue) Semblable à isEqualTo, sauf que cette fois la valeur affirmée doit avoir exactement la même valeur que la valeur cible. Ainsi, les objets et les tableaux ne passeraient pas le test, car ils pointeraient vers des références différentes.
isStrictlyNotEqualTo(someValue) L’assertion est un succès si la valeur affirmée est ne pas l’exact égal de someValue.
isDefined() L’assertion est un succès si la valeur affirmée est autre que undefined.
isUndefined() L’assertion est un succès si la valeur affirmée est undefined.
isNull() L’assertion est un succès si la valeur affirmée est null.
isNotNull() L’assertion est un succès si la valeur affirmée est autre chose que null.
isTrue() L’assertion est un succès si la valeur affirmée est true.
isFalse() L’assertion est un succès si la valeur affirmée est false.
isTruthy() L’assertion est un succès si la valeur affirmée n’est pas faux (voir l’assertion suivante pour plus de détails).
isFalsy() L’assertion est un succès si la valeur affirmée est falsy. Les fausses valeurs sont : undefined, null, false, NaN, 0et ''.

C’est le courant liste des assertions disponibles. Je suis sûr que d’autres seront ajoutés avec le temps.

En utilisant assertApi

Les assertApi() La fonction peut être utilisée pour vérifier si le code du modèle a effectué des appels à des API de modèle spécifiques.

Ceci est typique test de débit – vous vérifiez que certaines choses se passent dans l’ordre.

Prenons par exemple le code de modèle suivant :

const log = require('logToConsole'); const makeTableMap = require('makeTableMap'); const sendPixel = require('sendPixel'); const injectScript = require('injectScript');  // Log input to console log(data.input);  // Shape into object const obj = makeTableMap(data.input, 'propertyName', 'propertyValue');  if (obj.someValue) {   sendPixel('https://endPoint.com/?value=' + obj.someValue, data.gtmOnSuccess, data.gtmOnFailure); } else {   injectScript('https://endPoint.com/script.js', data.gtmOnSuccess, data.gtmOnFailure); } 

Pour tester le flux de ce modèle, vous pouvez utiliser un test comme celui-ci :

const mockData = {someValue: true};  runCode(mockData);  assertApi('logToConsole').wasCalled(); assertApi('makeTableMap').wasCalled(); assertApi('sendPixel').wasCalled(); assertApi('injectScript').wasNotCalled();  assertApi('gtmOnSuccess').wasCalled(); 

Faites attention à la dernière ligne – avec les modèles de balises, vous devriez toujours avoir un test qui vérifie si le gtmOnSuccess L’API a été appelée. Vous pouvez également tester gtmOnFailure dans certaines circonstances (en particulier, lorsque le modèle s’exécute data.gtmOnFailure()).

assertApi(apiName) renvoie un objet qui a deux assertions :

Affirmation La description
wasCalled() Succès si l’API a été appelée pendant l’exécution du test.
wasNotCalled() Réussite si l’API n’a PAS été appelée lors de l’exécution du test.

C’est très simple, de par sa conception. Vous pouvez uniquement vérifier si l’API a été appelée ou non.

Si vous voulez savoir s’il a été appelé avec une valeur spécifique, vous devez plutôt vous moquer des API.

La configuration du test

Lors de l’ouverture du Essais l’interface, vous verrez le Installer boîte. Vous pouvez l’utiliser pour établir des conditions qui s’appliquent à tous les tests. C’est un endroit idéal pour définir votre faux des objets par exemple, car vous n’avez alors pas à les écrire encore et encore à chaque test.

Configuration des tests

Je recommande d’utiliser généreusement Installer – il peut être beaucoup plus facile d’ajouter plus de tests au fur et à mesure que votre code passe par des itérations.

Tests en cours

Pour exécuter les tests, cliquez simplement sur le Exécuter des tests bouton en haut de la Essais voir, ou appuyez sur le bouton de lecture à côté de n’importe quel test individuel.

bouton exécuter les tests

La console enregistrera tous les résultats de test, alors faites-y très attention.

Échec des tests

Résumé

Donc. Pourquoi tester ? Eh bien, commençons par ce tweet énigmatique du responsable technique de Google Tag Manager, Brian :

J’imagine qu’à un moment donné, les tests deviendront obligatoires pour les modèles ajoutés à la galerie.

Un autre point est que les tests vous apportent, à vous développeur, du confort, puisque vous pouvez les utiliser pour valider que vous ne générez pas de code cassé.

Les tests unitaires sont problématiques – les tests reposent sur des données fictives plutôt que sur des cas d’utilisation réels (ceux-ci sont explorés avec l’intégration et fonctionnel essais). Leur seul but est de tester le code que vous écrivez, mais les tests sont effectués en utilisant… le code que vous écrivez.

Cependant, pensez aux utilisateurs de votre modèle. Lorsqu’ils téléchargent des mises à jour de votre modèle à partir de la galerie, ils sont convaincus que le code fonctionne comme avant. Une fois que votre test devient de plus en plus complexe, il sera de plus en plus difficile de gérer le code de branchement.

À ce stade, les tests unitaires vaudront chaque seconde que vous passerez avec eux.

Notez que les tests seuls ne font pas fonctionner votre code. Encore faut-il un bon modèle. Et écrire des tests juste pour la couverture n’a pas de sens – vous avez toujours besoin que les tests reflètent les cas d’utilisation réels.

J’ai hâte de voir comment le nouveau Essais La fonctionnalité évolue dans Google Tag Manager – c’est un grand pas en avant pour éviter les surprises et casser les erreurs dans votre code de modèle !

Source : www.simoahava.com

Articles similaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Bouton retour en haut de la page
Index