Autofixture: NSubstitute.ReceivedCalls() renvoyant une valeur erronée à l'aide d'AutoFixture.AutoNSubstitute

Créé le 28 mars 2018  ·  10Commentaires  ·  Source: AutoFixture/AutoFixture

Nous avons beaucoup de tests utilisant 'Class.ReceivedCalls()' et 'var tmp = Class.Received(int).Property;' pour vérifier le nombre d'appels. Dans la v3 d'AutoFixture, il a correctement signalé le nombre d'appels, en particulier de Propriétés, ce qu'il ne fait plus. Le nombre d'appels de méthodes semble toujours correct.
Étant donné que nous avons le code suivant :

    public interface IRunSpeed
    {
        int Speed { get; }
    }

    public class GetToDaChoppa
    {
        private readonly IRunSpeed _runSpeed;

        public GetToDaChoppa(IRunSpeed runSpeed)
        {
            _runSpeed = runSpeed ?? throw new ArgumentNullException(nameof(runSpeed));
        }

        public void DoItNow()
        {
            var runningSpeed = _runSpeed.Speed;
        }
    }

Et étant donné que nous avons les tests suivants :

        [Fact]
        public void DoItNow_WithOutAutoNSubstitute()
        {
            // Arrange
            var runSpeed = Substitute.For<IRunSpeed>();
            runSpeed.Speed.Returns(2);

            var sut = new GetToDaChoppa(runSpeed);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Single(runSpeed.ReceivedCalls());
        }

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Single(runSpeed.ReceivedCalls());

Mon AutoNSSubstituteDataAttribute ressemble à ceci :

        public class AutoNSubstituteDataAttribute : AutoDataAttribute
        {
            public AutoNSubstituteDataAttribute()
                : base(() => new Fixture()
                           .Customize(new AutoNSubstituteCustomization()))
            {
            }
        }

Le premier test 'DoItNow_WithOutAutoNSSubstitute' fonctionne correctement. Mais le deuxième test 'DoItNow_UsingAutoNSSubstitute' renvoie 2 pour 'ios.Received(1).Speed;'.
Il renvoie également 2 pour 'runSpeed.ReceivedCalls();'.
Pour cette raison, nous ne pouvons actuellement pas mettre nos solutions à niveau vers la v4, car nous avons instantanément plus de 1 000 tests d'échec par solution. Des conseils sur ce que le problème pourrait être ou où chercher le correctif?

question

Tous les 10 commentaires

Merci d'avoir déclenché le problème. En fait, ce problème n'a rien à voir avec AutoFixture, alors que nous en sommes également affectés 😕

Ce problème est principalement causé par xUnit et si vous réécrivez le test comme suit, il réussira :
```c#
[Théorie, AutoNSSubstituteData]
public void DoItNow_UsingAutoNSubstitute_CreateManually (appareil IFixture)
{
// Organiser
var runSpeed ​​= fixture.Freeze();
var sut = luminaire.Créer();
runSpeed.Speed.Returns (49);

//Act
sut.DoItNow();

//Assert
var tmp = runSpeed.Received(1).Speed;
Assert.Single(runSpeed.ReceivedCalls());

}
```

Lorsque vous exécutez des tests, xUnit essaie de formater le nom du test pour vous. S'il trouve que ce type n'est pas connu et n'a pas ToString() surcharge
image

Comme vous pouvez le remarquer, xUnit a récupéré la valeur de la propriété Speed pour bien vous montrer l'argument de test. Comme il s'agit d'une invocation habituelle, NSubstitute a compté cela comme un appel.

Il est assez étrange que vous n'ayez pas eu ce problème avec la v3, car rien n'a changé à cet égard. Je viens de tester xUnit2 + AutoFixture v3 et j'ai toujours le problème. Vous avez probablement également mis à niveau le xUnit.


En ce qui concerne une solution de contournement, j'ai une bonne et une mauvaise nouvelle. La bonne nouvelle est que cela sera résolu dans la prochaine version majeure de NSubsitute, car il a commencé à remplacer la méthode ToString() pour renvoyer un identifiant de proxy. En conséquence, xUnit ne touche plus les propriétés :
image

La mauvaise nouvelle est que NSubsitute v4 n'est pas encore sorti.

Je pourrais vous suggérer soit :

  • reporter la migration jusqu'à la sortie de NSubsitute v4 ;
  • utilisez le dernier code source master de NSubsitute, créez une version locale et utilisez votre propre NSubsitute.dll au lieu du package NuGet. Une fois la version 4 publiée, vous pouvez basculer vers le package NuGet.
  • si vous avez également mis à jour la version de xUnit (car cela pourrait expliquer pourquoi vous n'aviez pas ce problème auparavant), annulez cette modification et utilisez la version précédente de xUnit.

Je m'excuse pour la gêne occasionnée, mais, malheureusement, nous ne pouvons rien faire du côté d'AutoFixture pour résoudre ce problème.

viens de tester ça. enfin, nos tests affirmant que ReceivedCalls sont revenus pour avoir à nouveau un résultat positif.
Quelque chose qui échoue toujours sont les assertions sur Received(x) comme Received(1).Speed ​​Daniel mentionné ci-dessus.

snip_20180328181559

Avez-vous aussi une réponse/solution pour chapeau ?

Travaillez-vous sur le même projet ? :) Si oui, pouvez-vous expliquer comment vous avez résolu le problème ?

Les options suggérées ci-dessus devraient aider à résoudre les deux problèmes. S'ils ne le font pas, clarifiez le scénario.

pas réellement le même projet mais dans la même entreprise.
nous sommes sur xunit 1.9 + nsubstitute 2/3 depuis longtemps et nous avons finalement réussi à obtenir nos assemblys internes (paquets nuget donc un moyen simple de revenir à une ancienne version d'une seule référence n'est pas aussi facile qu'il y paraît être) pour être compatible avec xunit2. et maintenant nous avons ces deux problèmes.

Nous avons déjà essayé certaines choses et avons finalement pensé que cela devait avoir quelque chose à voir avec l'autofixation - à cause de l'attribut gelé et d'un nombre d'exécutions de test codé en dur - ou quelque chose comme ça.
après que Daniel ait ouvert ce dossier, il m'a posté le lien vers celui-ci.

tout comme vous l'avez décrit ci-dessus, j'ai remplacé la référence nuget 3.1 par celle du nouveau projet nsubstitute compilé. après cela, j'ai maintenant 118 au lieu de 178 tests qui échouent car Receiced (x) évalue toujours le mauvais nombre d'appels.

mon projet est actuellement basé sur .net 4.5.2 avec xunit2, la version actuelle du repo de nsubstitute et autofixture 4.2 référencée. tous les tests échouent pour les deux mêmes raisons - il y a également des échecs d'appels reçus dans les tests unitaires.
Je pense que je dois à nouveau y réfléchir de plus près quand je serai de retour au bureau (sortie pour Pâques).
peut-être que @dklinger peut décrire le scénario.

@dklinger @evilbaschdi Veuillez faire un suivi après avoir pu vérifier cela - il est assez intéressant de savoir pourquoi vous voyez toujours les problèmes, même après avoir appliqué le correctif.

Rouvrez le problème pour indiquer que l'enquête est toujours en cours.

Bon, j'ai testé ces dernières heures. Premièrement : Merci pour votre réponse, vos explications et vos suggestions - cela m'a beaucoup aidé.
J'ai testé la v4 de NSubstitute. Cela fonctionne pour mon exemple de code ci-dessus, mais ne résout pas entièrement le problème dans nos projets du monde réel. Le problème est que cela résout le problème du mauvais nombre d'appels reçus uniquement pour les appels SUT aux méthodes, pas aux propriétés.

Exemple de notre code qui fonctionne maintenant :

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

Mais si je change la méthode "GetToDaChoppa.DoItNow();" pour être une propriété "GetToDaChoppa.DoItNow", le ReceivedCallsCount est à nouveau +1 :

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            var x = sut.DoItNow;

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

Je pense que cela a encore à voir avec le nom de xUnit car nous obtenons celui-ci pour la mise en œuvre de la méthode de travail :
image

Et celle-là pour la mise en œuvre de la propriété non-fonctionnelle :
image

Il semble que xUnit appelle la propriété SUT pour récupérer sa valeur afin de l'utiliser pour le nom de test, mais il ne le fait pas pour les méthodes SUT. Ma première pensée était que cela avait à voir avec la valeur de retour de notre méthode "GetToDaChoppa()" qui était nulle. Mais même après l'avoir changé en "public int GetToDaChoppa()", xUnit ne l'appelle toujours pas pour obtenir une valeur de retour pour le test Name. C'est seulement un problème d'utilisation des propriétés.

Pour l'instant je suis à nouveau bloqué. Je suis tout à fait d'accord pour dire que ce n'est pas un problème d'AutoFixture. Mais à votre avis, connaissant mieux que nous tous les paquets, quelle serait votre suggestion ?

  • ouvrir un problème pour NSubstitute ?
  • ouvrir un problème pour xUnit2?
  • ou quelque chose de totalement nouveau ?

@dklinger Merci pour le suivi ! Pourriez-vous s'il vous plaît partager MCVE du code pour le dernier scénario qui échoue toujours ? Juste pour m'assurer que rien n'est manqué et que je n'ai pas mal compris les conditions.

Après cela, j'essaierai de déterminer pourquoi cela se produit et comment y remédier.

Merci.

Oui, bien sûr. C'est ici:

Système sous test :

    public interface IRunSpeed
    {
        int Speed { get; }
        void Dude();
        int Dude2();
    }

    public class GetToDaChoppa
    {
        private readonly IRunSpeed _runSpeed;

        public GetToDaChoppa(IRunSpeed runSpeed)
        {
            _runSpeed = runSpeed ?? throw new ArgumentNullException(nameof(runSpeed));
        }

        public int DoItNow
        {
            get
            {
                var runningSpeed = _runSpeed.Speed;
                return 0;
            }
        }
    }

Cas de test:

        [Theory, AutoNSubstituteData]
        public void DoItNowAsProperty_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            var x = sut.DoItNow;

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

Actuellement, j'essaie de suivre les discussions sur https://github.com/xunit/xunit/issues/1386 et https://github.com/AutoFixture/AutoFixture/issues/805. Peut-être que nous faisons quelque chose de mal ou qu'il existe un moyen de dire à xUnit de ne pas générer automatiquement le nom du test.
J'ai également essayé de créer mon propre TheoryAttribute en remplaçant la propriété DisplayName, mais cela n'aide pas non plus, car xUnit utilise toujours le nom généré automatiquement en interne. Mais c'est juste pour info et totalement sans rapport avec le code de démonstration ci-dessus.

        public sealed class MyTheoryAttribute : TheoryAttribute
        {
            public MyTheoryAttribute([CallerMemberName] string memberName = null)
            {
                DisplayName = "MyTestCase";
            }
        }

image

Bonjour, encore moi :)
Il s'agit bien de xUnit2 appelant toutes les propriétés et les champs des paramètres de méthode de test qui sont de type complexe. La ligne de code est celle-ci : https://github.com/xunit/assert.xunit/blob/2b70a9b0c5bb291f98472ec24cec437acf8d65c8/Sdk/ArgumentFormatter.cs#L156

Merci pour votre aide. J'ai rouvert un problème sur le xUnit-Repo où la discussion devrait se poursuivre : https://github.com/xunit/xunit/issues/1682

@dklinger Merci pour le scénario détaillé. C'est en effet assez délicat et il est difficile de le contourner d'une manière ou d'une autre. Du point de vue AutoFixture et NSubsitute, il n'y a aucune différence si le code est appelé quelque part profondément à l'intérieur de xUnit ou dans le corps de test.

Habituellement, comme solution de contournement, vous pouvez utiliser la fonctionnalité claire de NSubstitute :

runSpeed.ClearReceivedCalls();

Ce code doit être exécuté au prologue de chaque test où vous vérifiez le nombre exact d'appels. Cela fonctionne bien si vous avez quelques tests, cependant évidemment, si des milliers de tests sont concernés, cela ne servira pas à grand-chose

Il est dommage que l'intégration NSubsitute + xUnit2 + AutoFixture ne fonctionne pas bien et souffre de ce genre de problèmes. Le produit AutoFixture a été conçu pour simplifier la vie, plutôt que pour en faire un cauchemar 😕 J'espère que les gars de xUnit vous conseilleront un moyen rapide de résoudre le problème pour l'ensemble du projet.

Faites-moi savoir si vous pensez que nous pouvons faire quelque chose de notre côté pour améliorer la situation.

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

Questions connexes

Eldar1205 picture Eldar1205  ·  5Commentaires

josh-degraw picture josh-degraw  ·  4Commentaires

Ephasme picture Ephasme  ·  3Commentaires

tiesmaster picture tiesmaster  ·  7Commentaires

ecampidoglio picture ecampidoglio  ·  7Commentaires