Sinon: Sandbox renvoie l'erreur "Impossible de stuber une propriété propre inexistante"

Créé le 18 août 2017  ·  14Commentaires  ·  Source: sinonjs/sinon

J'ai juste essayé de passer de la 2.4.1 à la 3.2.1 et j'ai rencontré le problème suivant. Ce code fonctionne dans 2.4.1:

        const spy = sandbox.spy();
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: spy
            }
        });

Mais en 3.2.1, il lève une exception: TypeError: Cannot stub non-existent own property google

Ce n'est pas mentionné dans le guide de migration, donc cela semble être une régression.

Bug Regression

Commentaire le plus utile

~ Merci d'avoir rétabli ce comportement. ~

Voulait ajouter un cas d'utilisation qui prend en charge les propriétés de stubbing nonexistint.

Dans mon cas d'utilisation, je stubbing une propriété sur un objet de configuration. L'objet config possède diverses clés optionnelles et est initialisé en chargeant un fichier depuis la machine du développeur. Lorsque j'exécute un test particulier, j'ai besoin que l'une de ces clés soit définie sur une valeur connue, puis je souhaite restaurer l'objet du développeur tel qu'il était.

sandbox.stub(serverSecrets, 'the_key_i_need_set').value(fakeValue) est une manière très claire de transmettre cela. C'est bien que j'obtienne le même comportement, _ même si je ne sais pas à l'exécution si la clé est définie ou non.

Tous les 14 commentaires

Lié à # 1512.

Si la propriété n'existe pas, vous n'avez pas besoin de l'ajouter au bac à sable. Écrasez-le simplement. Mais oui, si cela a fonctionné avant et que nous n'avons pas explicitement dit que cela devrait changer, alors c'est une régression.

Je ne sais pas ce que nous devrions faire ici. Mettez à jour la documentation pour indiquer que le remplacement de valeurs non existantes n'a pas de sens et n'est pas pris en charge, ou le rend-il possible?

Si la propriété n'existe pas, vous n'avez pas besoin de l'ajouter au bac à sable. Écrasez-le simplement.

La bonne chose à propos de l'ajout de la propriété au bac à sable est que sinon, cela m'aide à garder mon environnement de test global propre entre chaque test via sandbox.restore() . C'est une fonctionnalité extrêmement utile, en particulier lorsque je traite avec des bibliothèques tierces comme Google Maps où je ne contrôle pas l'API. Ce serait formidable si cela pouvait fonctionner dans la ligne 3.x.

De plus, je viens de remarquer que j'ai commis le péché de ne pas donner d'exemple complet. Mon bac à sable est en cours de création au format 2.4.1:

let sandbox;

before(() => { sandbox = sinon.sandbox.create(); })
afterEach(() => { sandbox.restore(); })

Je ne sais pas si c'est important; excuses de ne pas l'avoir fourni plus tôt.

Je pense que dans les scénarios comme celui décrit par @ZebraFlesh , je préférerais que le montage de texte soit plus explicite.

// not so explicit, doesn't work with [email protected]
beforeEach(function() {
    const spy = sandbox.spy();
    sandbox.stub(window, 'google').value({
        maps: {
            LatLng: x => x,
            Map: spy
        }
    }); 
});
// more explicit, works with sinon<strong i="9">@2</strong>, sinon<strong i="10">@3</strong>
function setGoogleMapsFixture(sandbox) {
    window.google = {
        maps: {
            LatLng: x => x,
            Map: sandbox.spy()
        }
    };
}

function removeGoogleMapsFixture() {
    delete window.google;
}

beforeEach(function() {
    setGoogleMapsFixture(sandbox)
});

// not using afterEach, as this only needs to happen
// after the last test in this block is run
after(function() {
    removeGoogleMapsFixture();
});

Avec un réglage plus explicite de l'appareil comme indiqué ci-dessus, vous n'avez pas besoin d'une fonctionnalité dans Sinon qui permettrait le remplacement de propriétés propres non existantes.

Je ne sais pas ce que nous devrions faire ici. Mettez à jour la documentation pour indiquer que le remplacement de valeurs non existantes n'a pas de sens et n'est pas pris en charge, ou le rend-il possible?

Bien que je reconnaisse que cela peut être pratique dans certains scénarios (comme celui décrit par @ZebraFlesh), je pense que le remplacement de propriétés propres non existantes est susceptible de conduire à des erreurs dans les tests, où le test réussit parce que l'auteur a mal tapé le nom de la propriété existante qu'ils visaient à stuber. Nous devons viser à éliminer la possibilité d'erreurs là où nous le pouvons sans être trop restrictifs.

Je pense que le remplacement de propriétés propres non existantes ne devrait pas être pris en charge. Nous devons mettre à jour la documentation.

@mroderick Je suis d'accord avec vous en ce sens que cela peut supportons déjà pour les stubs normaux. Si nous devons arrêter de soutenir ce comportement, nous devons également le supprimer, pour être cohérent. Il serait étrange de ne prendre en charge cette fonctionnalité qu'en dehors des sandbox, car les sandbox ajoutent généralement des possibilités. Et la suppression du support est une fonctionnalité de rupture, donc une bosse majeure serait également nécessaire.

Alors soit:

  • supprimer la coche rapidement pour les bacs à sable pour corriger cette fonctionnalité de rupture

ou et(?)

  • supprimer la fonctionnalité pour les stubs normaux et sandbox

    • publier une nouvelle version majeure avec des documents mis à jour

Avec un réglage plus explicite de l'appareil comme indiqué ci-dessus, vous n'avez pas besoin d'une fonctionnalité dans Sinon qui permettrait le remplacement de propriétés propres non existantes.

Je pense que cela fonctionne bien si votre appareil ne varie jamais selon vos tests. Cependant, mon appareil le fait. Un exemple simple couvre à la fois les cas de réussite et d'échec:

it('handles the success case', () => {
        const spy = sandbox.spy();
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: spy
            }
        });
        // ... test, including asserting that the spy was called
});

it('handles the failure case', () => {
        const msg = 'test error';
        sandbox.stub(window, 'google').value({
            maps: {
                LatLng: x => x,
                Map: sandbox.stub().throws(new Error(msg))
            }
        });
        // ... test, ignoring spy calls and instead focusing on error handling
});

Le comportement dans 2.x a l'avantage que tout est correctement nettoyé après chaque test via sandbox.restore() . En utilisant l'exemple de configuration de luminaire plus explicite décrit ci-dessus, je suppose que vous pouvez supprimer la propriété non propre dans un hook afterEach pour obtenir le même effet.

Pour résoudre le problème de l'introduction d'erreurs potentielles en tapant par inadvertance le nom d'une propriété existante, sinon pourrait modifier l'API publique:

  • stub.ownValue() : les stubs ne possèdent que les propriétés, les lancers pour les propriétés non propres
  • stub.value() : stubs uniquement les propriétés non propres, lance pour les propres propriétés

L'API devient plus explicite et le consommateur est obligé de choisir l'outil approprié pour la tâche à accomplir.

Ceci est très lié à la discussion dans # 1508 (bien qu'il traite des stubs normaux) h, où @lucasfcosta a le point de vue opposé - que nous ne devrions pas lancer_ pour les propriétés undefined . Quelle que soit notre destination, je crois fermement que _nous devons être cohérents_ dans les API de stubbing pour les stubs et sandbox normaux. Nous ne devons pas l'appuyer dans un cas et pas dans l'autre.

À l'heure actuelle, la situation est:

  • Les stubs normaux utilisaient la version 1.x, mais cela a changé dans la version 2.0 et ne le lance plus maintenant
  • les bacs à sable n'ont pas l'habitude de lancer, mais ont commencé à lancer 3.1 (?)

Donc, pendant un certain temps, nous avons eu la parité des fonctionnalités, mais nous l'avons encore perdue ... Je ne pense pas que ce zig-zag soit très bénéfique pour les utilisateurs, nous devrions donc engager cette discussion. Bien que je sois d'accord avec Morgan en ce sens que cela pourrait conduire à des tests plus spécifiques, je n'aime pas abandonner un comportement pour deux versions majeures, puis le rajouter à nouveau. Je pense que cela ferait le moins de bruit (correctifs pour les clients, questions / problèmes sur ce tracker) juste pour inverser cette régression.

Bien que je comprenne les inconvénients, il semble qu'il existe une solution de contournement simple avec des changements de code minimes.

before(function() {
  window.google = 'This is a placeholder for sinon to overwrite.';
});

after(function() {
  delete window.google;
});

Cela permet au code sinon de rester inchangé.

Régression vs documentation médiocre

Cela semble être un comportement attendu car il existe des tests pour cela. Nous devrions mettre à jour les documents pour refléter cela à mon avis. Il est venu dans un majeur donc les changements brusques sont tolérés.

@fearphage Le maintien du statu quo signifie que le stubbing des champs non existants est un comportement non pris en charge pour les sandbox, alors qu'il est pris en charge pour les stubs normaux. N'est-ce pas un peu dommage que les deux ensembles de fonctionnalités ne s'alignent pas?

La résolution a été implémentée dans # 1557

J'ai lu les différents threads et je peux voir pourquoi cela s'est produit, mais c'est une vraie douleur dans Typescript où vous avez souvent des fonctions qui sont implémentées sur un prototype de classe, auquel cas sinon crache le mannequin même si tout semble correct sage (puisque keyof YourType autorisera volontiers toutes les fonctions publiques qui sont définies plus bas dans la chaîne de prototypes).

Je comprends que Typescript n'est probablement pas une priorité pour vous, mais même dans JS, il semble contre-intuitif que myObject.callMe() s'exécutera parfaitement, alors que sinon.stub(myObject, "callMe") ne le sera pas dans ce cas. Je préférerais ne pas avoir à aller chercher comment cet objet particulier a été assemblé pour que je sache comment le remplacer.

Je pense vraiment que c'est un cas d'utilisation important pour lequel tracer un chemin heureux, étant donné que les classes reçoivent plus de support natif dans JS.

Si vous obtenez une erreur indiquant que la méthode n'est pas définie sur l'objet, vous savez que l'erreur provient probablement du prototype. Ensuite, modifier directement l'objet en utilisant myObject.callMe = sinon.stub(); ne semble pas être un tracas à mon humble avis ... Cela devrait également vous éviter de créer des fonctions de nettoyage / démontage, car le prototype n'a jamais été changé.

Oui, je suppose que ce n'est pas trop difficile à contourner, cela me met simplement plus de charge cognitive pour être conscient de la façon dont les choses sont mises en œuvre.

Cela semble également inattendu, de sorte que j'ai ressenti le besoin d'ajouter un commentaire dans le test pour expliquer pourquoi le code de stubbing sur 2 lignes consécutives pour le même objet était différent, et pourquoi je supprimais manuellement l'un des stubs dans mon démontage, mais l'autre était géré par le bac à sable.

~ Merci d'avoir rétabli ce comportement. ~

Voulait ajouter un cas d'utilisation qui prend en charge les propriétés de stubbing nonexistint.

Dans mon cas d'utilisation, je stubbing une propriété sur un objet de configuration. L'objet config possède diverses clés optionnelles et est initialisé en chargeant un fichier depuis la machine du développeur. Lorsque j'exécute un test particulier, j'ai besoin que l'une de ces clés soit définie sur une valeur connue, puis je souhaite restaurer l'objet du développeur tel qu'il était.

sandbox.stub(serverSecrets, 'the_key_i_need_set').value(fakeValue) est une manière très claire de transmettre cela. C'est bien que j'obtienne le même comportement, _ même si je ne sais pas à l'exécution si la clé est définie ou non.

Cette page vous a été utile?
0 / 5 - 0 notes