Comment se moquer correctement d'un IEnumerable<T> ?

Mon dernier désastre à cause des moqueries n'est pas le fait que j'ai besoin de pousser les résultats dans un objet fictif de IEnumerable<T>.

Voici un exemple (démonstration uniquement de IEnumerable<T>, pas vraiment de bons tests basés sur l'interaction !) :

using System;
using System.Collections.Generic;
using Rhino.Mocks;
using MbUnit.Framework;

[TestFixture]
public class ZooTest
{
    [Test]
    public void ZooCagesAnimals()
    {
        MockRepository mockery = new MockRepository();

        IZoo zoo = new Zoo();

        // This is the part that feels wrong to create.
        IList<IAnimal> mockResults = mockery.DynamicMock<IList<IAnimal>>();
        IAnimal mockLion = mockery.DynamicMock<IAnimal>();
        IAnimal mockRhino = mockery.DynamicMock<IAnimal>();

        using (mockery.Record())
        {
            Expect.Call(zoo.Animals)
                .Return(mockResults)
                .Repeat.Once();
        }

        using (mockery.Playback())
        {
            zoo.CageThe(mockLion);
            zoo.CageThe(mockRhino);

            Assert.AreEqual(mockResults, new List<IAnimal>(zoo.Animals));
        }       
    }
}


public class Zoo : IZoo
{
    private IList<IAnimal> animals = new List<IAnimal>();

    public void CageThe(IAnimal animal)
    {
        animals.Add(animal);
    }

    public IEnumerable<IAnimal> Animals
    {
        get
        {
            foreach(IAnimal animal in animals)
            {
                yield return animal;
            }
        }
    }
}

public interface IAnimal
{
}

public interface IZoo
{
    IEnumerable<IAnimal> Animals { get;}
    void CageThe(IAnimal animal);
}

Je n'aime pas la façon dont je l'ai fait fonctionner pour les raisons suivantes :

  • Consommer les résultats IEnumerable<IAnimal> en IList<IAnimal> - parce que je comprends que cela met les résultats à vérifier sur le tas.
  • Configuration du contenu des résultats - ce que je comprends également ; mais mon point principal est de tester que Zoo.Animals renvoie IEnumerable<IAnimal>, et mieux encore, qu'il utilise yield return à l'intérieur.< /li>

Des suggestions pour faire mieux ou plus simplement ?

Modifier : j'essaie de déterminer la manière optimale de tester l'interaction entre IEnumerable<T> et tout ce que j'utilise. Je n'essaie pas de tester que Zoo peut contenir des animaux, plutôt que Zoo expose comme IEnumerable<IAnimal>, et que 8 s'habitue également.

请先 登录 后评论

2 réponses

Rytmis

Si vous testez la mise en œuvre, pourquoi essayez-vous de vous en moquer ? Pourquoi ne pas simplement CageThe(IAnimal) et ensuite vérifier que Animals contient cet IAnimal ?

Je comprends que vous vous moquez des IAnimals, vu qu'apparemment vous n'avez pas encore d'animaux concrets avec lesquels jouer, mais pourquoi ne pas simplement en faire des souches, car évidemment vous ne vous attendez pas à ce qu'il leur arrive autre chose en plus d'être mis dans la liste ?

Modifier : Quelque chose à peu près dans ce sens (non testé, peut ne pas compiler, peut manger votre chien, etc.) :

[TestFixture]
public class ZooTest 
{
    [Test]
    public void ZooCagesAnimals()
    {
        MockRepository mockery = new MockRepository();

        IAnimal mockLion = mockery.Stub<IAnimal>();
        IAnimal mockRhino = mockery.Stub<IAnimal>();

        IZoo zoo = new Zoo();

        zoo.CageThe(mockLion);
        zoo.CageThe(mockRhino);

        List<IAnimal> animals = new List<IAnimal>(zoo.Animals);
        Assert.IsTrue(animals.Contains(mockLion));
        Assert.IsTrue(animals.Contains(mockRhino));
    }
}
请先 登录 后评论
Rytmis

Je ne vois pas très bien comment vous comptez mettre en place des attentes fictives sur un objet qui n'est pas fictif pour commencer. De plus, vous configurez l'attente pour renvoyer un IList, ce qui n'est pas vraiment ce qui se passe lorsque le compilateur génère un itérateur.

Si vous souhaitez tester spécifiquement l'itérateur, vous devriez probablement

Assert.IsNotNull(zoo.Animals);

Et ensuite, vérifiez que l'énumérateur énumère réellement toutes les choses que vous avez ajoutées au Zoo. C'est ce que je cherchais là-bas. :)

Je ne sais pas s'il est même possible de tester si yield return est appelé, car yield return n'est qu'un sucre syntaxique pour un IEnumerable généré par le compilateur. Par exemple, appeler

zoo.Animals.GetEnumerator();

n'exécutera aucun des codes que vous avez écrits dans l'énumérateur. La première fois que cela se produit, c'est lors du premier appel à IEnumerator.MoveNext();

Maintenant, si vous essayez de tester l'interaction entre le Zoo concret et l'IEnumerable contenu par ce Zoo, vous devez faire de l'IEnumerable un champ sur Zoo et injecter un IEnumerable fictif dans ce champ au lieu d'implémenter directement un IEnumerable concret dans Zoo.

J'espère que cela vous sera utile.

请先 登录 后评论