10 Stimmen

Wie spottet man eine Schnittstelle, die IEnumerable erweitert

Ich verwende Moq und habe die folgende Schnittstelle:

public interface IGameBoard : IEnumerable<PieceType>
{
    ...  
}
public class GameBoardNodeFactory
{
    public virtual GameBoardNode Create (int row, int column, IGameBoard gameBoard)
    {
        ...
    }
}

Dann habe ich einen Test wie diesen:

var clonedGameBoardMock = new Mock<IGameBoard> (MockBehavior.Loose);
var gameBoardNodeFactoryMock = new Mock<GameBoardNodeFactory> ();
gameBoardNodeFactoryMock.Setup (x =>
    x.Create (
        position.Row,
        position.Column,
        clonedGameBoardMock.Object)).Returns (new GameBoardNode { Row = position.Row, Column = position.Column });

Aber dann gameBoardNodeFactoryMock.Object.Create (position.Row, position.Column, clonedGameBoardMock.Object) wirft eine NullReferenceException. Ich habe versucht, eine Attrappe für die IGameBoard zu erstellen, so dass es nicht IEnumerable<PieceType> Schnittstelle erweitern und dann funktioniert es.

Für jede Hilfe sind wir dankbar.

12voto

Dan Bryant Punkte 27022

Sie müssen ein Setup für GetEnumerator() erstellen, wenn es aufgerufen wird. Etwas wie:

var mockPieces = new List<PieceType>;
clonedGameBoardMock.Setup(g => g.GetEnumerator()).Returns(mockPieces.GetEnumerator());

Ich bin mir nicht sicher, ob das in diesem Fall das Problem ist, aber es lohnt sich, dies zu beachten, falls Sie jemals eine Spöttelei durchführen müssen. IEnumerable<T> .

4voto

Jeroen Punkte 56568

Die Antwort von @DanBryant war auch der Schlüssel zu unserer Lösung. Allerdings könnte der Enumerator in diesem Fall versehentlich wiederverwendet werden. Stattdessen schlage ich die Verwendung von:

clonedGameBoardMock.Setup(g => g.GetEnumerator()).Returns(() => mockPieces.GetEnumerator());

Hier ist eine vollständige Repro (neue Klassenbibliothek mit NUnit 2.6.4 und Moq 4.2):

public interface IMyThing<T> : IEnumerable<T>
{
    string Name { get; set; }

    IMyThing<T> GetSub<U>(U key);
}

public interface IGenericThing
{
    string Value { get; set; }
}

public class Pet
{
    public string AnimalName { get; set; }
}

public class Unit
{
    public IEnumerable<Pet> ConvertInput(IMyThing<IGenericThing> input)
    {
        return input.GetSub("api-key-123").Select(x => new Pet { AnimalName = x.Value });
    }
}

[TestFixture]
public class Class1
{
    [Test]
    public void Test1()
    {
        var unit = new Unit();
        Mock<IMyThing<IGenericThing>> mock = new Mock<IMyThing<IGenericThing>>();
        Mock<IMyThing<IGenericThing>> submock = new Mock<IMyThing<IGenericThing>>();

        var things = new List<IGenericThing>(new[] { new Mock<IGenericThing>().Object });

        submock.Setup(g => g.GetEnumerator()).Returns(() => things.GetEnumerator());
        mock.Setup(x => x.GetSub(It.IsAny<string>())).Returns(submock.Object);

        var result = unit.ConvertInput(mock.Object);
        Assert.That(result, Is.Not.Null.And.Not.Empty);
        Assert.That(result, Is.Not.Null.And.Not.Empty); // This would crash if the enumerator wasn't returned through a Func<>...
    }
}

Was auch immer es wert ist / um diese Frage dem einen einsamen Googler mit dem gleichen Problem wie ich zu stellen: das obige ist eine abstrahierte Version des Couchbase .NET Client's IView<T> Schnittstelle, die auch Folgendes implementiert IEnumerable<T> .

0voto

Jon Punkte 363

Eine Nullreferenz in dieser Situation bedeutet in der Regel, dass Ihre Vorgaben nicht erfüllt wurden. Das heißt, es wurde nie mit den genauen Werten aufgerufen, für die Sie es eingerichtet haben. Um dies zu debuggen, würde ich Ihre Übereinstimmung weniger einschränkend machen, indem ich It.IsAny() und so weiter verwende, um sicherzustellen, dass der Test bei jedem Aufruf der nachgebildeten Funktion übereinstimmen wird. In den meisten Fällen ist das gut genug. Gibt es einen Grund, warum Sie versuchen, auf bestimmte Werte abzustimmen?

0voto

Zoliqa Punkte 985

Okay, falls es jemanden interessiert, ich habe Moq auf Version 4 aktualisiert und jetzt funktioniert alles wie erwartet.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X