Comment puis-je configurer un appel d'un Equals avec un type spécifique remplaçant Egaux dans moq?

StackOverflow https://stackoverflow.com/questions/1476233

Question

Travailler avec l'amende moq-cadre moqueur, je suis tombé sur une facette quelque peu surprenant (et je n'aime pas les surprises). Je suis moqueur une classe qui devrait être ajouté à une collection après une méthode d'appel, comme suit:

public class SomeClass{

}

public class Container {
    private List<SomeClass> classes = new List<SomeClass>();

    public IEnumerable<SomeClass> Classes {
        get {
            return classes;
        }
    }

    public void addSomeClass(SomeClass instance) {
        classes.Add(instance);
    }
}

[Test]
public void ContainerContainsAddedClassAfterAdd() {
    var mockSomeClass = new Mock<SomeClass>();  
    mockSomeClass.Setup(c => c.Equals(mockSomeClass.Object)).Return(true);

    var Container = new Container();
    Container.addSomeClass(mockSomeClass.Object);

    Assert(Container.Classes.Contains(mockSomeClass.Object));
}

Cela fonctionne bien, la maquette est ajouté à la collection du Container et la mise en place de la méthode Equals sur la maquette fait que le IEnumerable.Contains() return true. Cependant, il y a toujours une complication. La classe que je suis vraiment moqueur en est pas aussi simple que notre SomeClass. Il est quelque chose comme ceci:

public class SomeClassOverridingEquals{
    public virtual Equals(SomeClassOverridingEquals other) {
        return false;   
    }

    public override Equals(object obj) {
        var other = obj as SomeClassOverridingEquals;

        if (other != null) return Equals(other); // calls the override
        return false;
    }
}

[Test]
public void ContainerContainsAddedClassOverridingEqualsAfterAdd() {
    var mockSomeClass = new Mock<SomeClassOverridingEquals>();  
    mockSomeClass.Setup(c => c.Equals(mockSomeClass.Object)).Return(true);

    var Container = new Container();
    Container.addSomeClass(mockSomeClass.Object);

    Assert(Container.Classes.Contains(mockSomeClass.Object)); // fails
}

La classe contient une dérogation pour la méthode Equals pour son propre type spécifique, et la méthode Setup pour la maquette ne semble pas être en mesure de se moquer que méthode spécifique (uniquement remplaçant la Equals(object) plus générale). Ainsi, le test échoue.

Je l'ai trouvé si loin aucun moyen de contourner ce modèle tout à fait commun, autre que la réécriture de la classe de ne pas utiliser enchevêtrements égale.

Je n'aime pas.

Quelqu'un a des idées?

Était-ce utile?

La solution

Je ne pense pas que le problème est dû à Moq, mais plutôt avec la méthode d'extension Contient. Même si vous avez surchargé avec une surcharge Égal plus spécifique, Enumerable.Contains finit par appeler List<T>.Contains parce que la propriété des classes est vraiment soutenu par un List<T>.

List<T>.Contains est mis en œuvre en appelant EqualityComparer<T>.Default.Equals, et je pense que la valeur par défaut pour appeler la méthode Equals héritée de System.Object - qui est: non pas celui de vos maquettes de remplacements, mais celui qui prend une System.Object en entrée <. / p>

Parcourant la mise en œuvre de EqualityComparer<T>.Default dans le réflecteur, il semble que si elle a un cas particulier pour les types d'application IEquatable<T>, donc si vous ajoutez cette interface à votre classe (il a déjà la méthode appropriée), il peut se comporter différemment.

Autres conseils

Mark Seemann est correct. Vous voulez vous assurer que vous laissez entendre Moq à la surcharge droite:

mockSomeClass.Setup(c => c.Equals((object)mockSomeClass.Object)).Return(true);

Comment est-il difficile de créer une instance de SomeClass? Ne serait-il plus logique que d'utiliser l'objet réel? Si possible, ce serait mieux, car il ne change pas le comportement particulier qui fait partie de la relation entre le conteneur et la classe.

Vous pouvez itérer la collection retournée et il suffit de regarder pour un objet qui est la même chose que l'instance simulée. Cela éviterait d'avoir à contourner un comportement spécialisé.

Créer un ISomeClass d'interface et de rendre votre conteneur interface utilisation à la place. De cette façon, vous allez réaliser deux choses:

  1. Vous pourrez facilement se moquer par Mock<ISomeClass> et en fait juste tester l'unité de fonctionnalité de votre conteneur
  2. unir test de mieux en fait conteneur de test unitaire séparé de tester la mise en œuvre effective de la fonctionnalité de classe SomeClass.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top