Hoe kan ik overbelaste methode aanroepen instellen in Moq?

Ik probeer een mapping-interface IMapper te bespotten:

public interface IMapper {
    TBar Map(TFoo foo);
    TFoo Map(TBar bar);
}

In mijn test, zet ik de mock mapper op om een ​​aanroep van elk te verwachten (rondom een ​​NHibernate update bewerking):

//...
_mapperMock.Setup(m => m.Map(fooMock.Object)).Returns(barMock.Object);
_mapperMock.Setup(m => m.Map(barMock.Object)).Returns(fooMock.Object);
//...

Wanneer de tweede Map aanroep wordt gedaan, gooit de mapper mock omdat het slechts één aanroep verwacht.

Terwijl ik tijdens het instellen tijdens runtime kijk naar de mapper, kan ik kijken hoe de Map (TFoo foo) overload wordt geregistreerd en zie je hem vervangen wanneer de Map (TBar bar) overbelasting is ingesteld.

Is dit een probleem met de manier waarop Moq setup afhandelt of is er een andere syntax die ik in dit geval moet gebruiken?

EDIT Here is the actual instantiation code from the test constructor:

public class TestClass {
    private readonly MockRepository _repository = new MockRepository(MockBehavior.Strict);

    public TestClass() {
        //...
        _mapperMock = _repository.Create
            >();
        //...
     }
}

EDIT 2

Hier is een volledig falende testcase:

public interface IMapper {
    TFoo Map(TBar bar);
    TBar Map(TFoo foo);
}

public class Foo {
    public override int GetHashCode() {
       //return base.GetHashCode();
        return 1;
    }
}

public class Bar {
    public override int GetHashCode() {
       //return base.GetHashCode();
        return 2;
    }
}

[Test]
public void TestIt()
{
   //Arrange
    var _mapperMock = new Mock>(MockBehavior.Strict);
    var fooMock = new Mock();
    var barMock = new Mock();

    _mapperMock.Setup(m => m.Map(fooMock.Object)).Returns(barMock.Object);
    _mapperMock.Setup(m => m.Map(barMock.Object)).Returns(fooMock.Object);

   //Act - breaks on first line below this comment
    var bar = _mapperMock.Object.Map(fooMock.Object);
    var foo = _mapperMock.Object.Map(barMock.Object);

   //Assert
    _mapperMock.Verify(x => x.Map(fooMock.Object), Times.Once());
    _mapperMock.Verify(x => x.Map(barMock.Object), Times.Once());
}

Als ik de GetHashCode() negeren op Foo of Bar , of op beide, de testcase doorgeeft. Of, als ik Mock s van Foo en Bar niet gebruik, wordt de testcase doorgegeven.

EDIT 3 I opened Moq Issue 347 against this problem, with more detailed test cases.

4
Het zijn verschillende typen, overerfd van verschillende subtypen. Ik zie dat de methode-resolutie correct varieert als ik de test in de foutopsporing doorloop, maar de eerste aanroep wordt eenvoudigweg vervangen door de tweede wanneer de tweede Setup aanroep wordt gedaan. Ik zal ook de eigenlijke instantiatiecode toevoegen.
toegevoegd de auteur arootbeer, de bron
Kunt u alstublieft uw code opnemen op de plek waar u _mapperMock maakt? Bedankt.
toegevoegd de auteur Kevin Aenmey, de bron
De enige keer dat ik zie dat uw bovenstaande code niet werkt, is of de generieke TFoo en TBar van hetzelfde type zijn. Bijv. var _mapperMock = new Mock > (); Als ze anders zijn (bijvoorbeeld var _mapperMock = new Mock > ();), zou het moeten werken
toegevoegd de auteur Kevin Aenmey, de bron

1 antwoord

Ik weet niet zeker hoe uw klassen Foo en Bar eruit zien, maar deze test slaagt voor mij (Moq 4.0.10827.0, de nieuwste in NuGet)

using Moq;
using NUnit.Framework;

namespace ConsoleApplication1
{
    [TestFixture]
    public class Tests
    {
        [Test]
        public void TestIt()
        {
           //Arrange
            var _mapperMock = new Mock>();
            var fooMock = new Mock();
            var barMock = new Mock();

            _mapperMock.Setup(m => m.Map(fooMock.Object)).Returns(barMock.Object);
            _mapperMock.Setup(m => m.Map(barMock.Object)).Returns(fooMock.Object);

           //Act
            var bar = _mapperMock.Object.Map(fooMock.Object);
            var foo = _mapperMock.Object.Map(barMock.Object);

           //Assert
            Assert.AreSame(bar, barMock.Object);
            Assert.AreSame(foo, fooMock.Object);

            _mapperMock.Verify(x => x.Map(fooMock.Object), Times.Once());
            _mapperMock.Verify(x => x.Map(barMock.Object), Times.Once());
        }
    }

    public class Bar
    {
    }

    public class Foo
    {
    }
}
0
toegevoegd
Het standaardgedrag voor je mock is Losse . Is de bovenstaande test nog steeds geslaagd als _mapperMock gedrag Strikte gebruikt?
toegevoegd de auteur arootbeer, de bron
Interessant ... Ik vraag me af of het iets te maken heeft met het gebruik van de MockRepository om de mapper te definiëren. Ik zal proberen direct een HTML-code te maken voor _mapperMock om te zien of dat het verschil maakt.
toegevoegd de auteur arootbeer, de bron
Ik doorbreek nog steeds mijn geval met de DLL van de website (we gebruiken anders een kopie die is gebouwd met de AFAICT-bron) en gebruik een lokaal geïnstantieerde mock van de mapper. Ik heb echter ontdekt dat het iets te maken heeft met de basisklassen die ten grondslag liggen aan Foo en Bar - als ik de test voer zonder de basisklassen gespecificeerd, het zal passeren, maar als ik specificeer dat Foo en Bar de respectieve basisklassen uitbreiden, mislukt de test.
toegevoegd de auteur arootbeer, de bron
Ik destilleerde het basistype naar de oorzaak van de fout en publiceerde de volledige code als een bewerking.
toegevoegd de auteur arootbeer, de bron
Ik heb uw antwoord geaccepteerd omdat dit tot mijn nader onderzoek leidde en omdat ik uw code voor de testcase heb opgelicht: P
toegevoegd de auteur arootbeer, de bron
Ik heb het bovenstaande getest met var _mapperMock = new Mock > (MockBehavior.Strict); en het wordt nog steeds verwerkt.
toegevoegd de auteur Kevin Aenmey, de bron
Stel dat u de code voor Foo en Bar en hun basistypen plaatst waardoor deze mislukt?
toegevoegd de auteur CodingWithSpike, de bron