434 Stimmen

Unterschiedliche Rückgabewerte beim ersten und zweiten Mal mit Moq

Ich habe einen Test wie diesen:

    [TestCase("~/page/myaction")]
    public void Page_With_Custom_Action(string path) {
        // Arrange
        var pathData = new Mock<IPathData>();
        var pageModel = new Mock<IPageModel>();
        var repository = new Mock<IPageRepository>();
        var mapper = new Mock<IControllerMapper>();
        var container = new Mock<IContainer>();

        container.Setup(x => x.GetInstance<IPageRepository>()).Returns(repository.Object);

        repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(() => pageModel.Object);

        pathData.Setup(x => x.Action).Returns("myaction");
        pathData.Setup(x => x.Controller).Returns("page");

        var resolver = new DashboardPathResolver(pathData.Object, repository.Object, mapper.Object, container.Object);

        // Act
        var data = resolver.ResolvePath(path);

        // Assert
        Assert.NotNull(data);
        Assert.AreEqual("myaction", data.Action);
        Assert.AreEqual("page", data.Controller);
    }

GetPageByUrl läuft zweimal in meinem DashboardPathResolver Wie kann ich Moq sagen, dass er zurückkehren soll? null das erste Mal und pageModel.Object die zweite?

6voto

Torbjörn Kalin Punkte 1916

En akzeptierte Antwort sowie die SetupSequence-Antwort behandelt zurückkehrende Konstanten.

Returns() hat einige nützliche Überladungen, mit denen Sie einen Wert zurückgeben können, der auf den Parametern basiert, die an die nachgebildete Methode gesendet wurden. Basierend auf die Lösung in der akzeptierten Antwort, hier ist eine weitere Erweiterungsmethode für diese Überladungen.

public static class MoqExtensions
{
    public static IReturnsResult<TMock> ReturnsInOrder<TMock, TResult, T1>(this ISetup<TMock, TResult> setup, params Func<T1, TResult>[] valueFunctions)
        where TMock : class
    {
        var queue = new Queue<Func<T1, TResult>>(valueFunctions);
        return setup.Returns<T1>(arg => queue.Dequeue()(arg));
    }
}

Leider müssen Sie bei der Verwendung dieser Methode einige Vorlagenparameter angeben, aber das Ergebnis ist dennoch recht gut lesbar.

repository
    .Setup(x => x.GetPageByUrl<IPageModel>(path))
    .ReturnsInOrder(new Func<string, IPageModel>[]
        {
            p => null, // Here, the return value can depend on the path parameter
            p => pageModel.Object,
        });

Erstellen Sie Überladungen für die Erweiterungsmethode mit mehreren Parametern ( T2 , T3 usw.), falls erforderlich.

4voto

Saravanan Punkte 852

Ich bin wegen des gleichen Problems mit etwas anderen Anforderungen hierher gekommen.
Ich muss mir unterschiedliche Rückgabewerte von Mock bei unterschiedlichen Eingabewerten und fand eine Lösung, die IMO lesbarer ist, da sie die deklarative Syntax von Moq verwendet (linq to Mocks).

public interface IDataAccess
{
   DbValue GetFromDb(int accountId);  
}

var dataAccessMock = Mock.Of<IDataAccess>
(da => da.GetFromDb(It.Is<int>(acctId => acctId == 0)) == new Account { AccountStatus = AccountStatus.None }
&& da.GetFromDb(It.Is<int>(acctId => acctId == 1)) == new DbValue { AccountStatus = AccountStatus.InActive }
&& da.GetFromDb(It.Is<int>(acctId => acctId == 2)) == new DbValue { AccountStatus = AccountStatus.Deleted });

var result1 = dataAccessMock.GetFromDb(0); // returns DbValue of "None" AccountStatus
var result2 = dataAccessMock.GetFromDb(1); // returns DbValue of "InActive"   AccountStatus
var result3 = dataAccessMock.GetFromDb(2); // returns DbValue of "Deleted" AccountStatus

2voto

Wir können einfach eine Variable deklarieren mit int als Datentyp. Initialisieren Sie ihn mit zero und erhöhen Sie dann den Wert wie folgt:

int firstTime = 0;
            repository.Setup(_ => _.GetPageByUrl<IPageModel>(path)).Returns(() =>
            {
                if (firstTime == 0)
                {
                    firstTime = 1;
                    return null;
                }
                else if(firstTime == 1)
                {
                    firstTime = 2;
                    return pageModel.Object;
                }
                else
                {
                    return null;
                }
            });

0voto

NitinSingh Punkte 1957

In manchen Fällen muss die aufgerufene Funktion verschiedene Datentypen zurückgeben, die auf Bedingungen beruhen, die Sie nicht über die Funktion selbst festlegen können. Wenn die Funktion Parameter akzeptiert, können diese als Konditionale verwendet werden, um verschiedene Daten zu erhalten.

In meinem Fall hatte ich einen Webapi-Aufruf, die ich brauche, um zu spötteln; früher funktionierte es gut auf der Grundlage der Eingabeparameter, jedoch eines schönen Tages, diese Parameter wurden in Anfrage-Header konvertiert. Da ich also keinen Callback (keine Funktionsparameter) zur Verfügung stellen konnte, habe ich mir einen anderen Ansatz ausgedacht, wie folgt

[Früher, als API noch Parameter hatte]

this.mockedMasterAPICalls.Setup(m => m.GetCountries(It.Is<int>(ou => ou == 2), It.Is<int>(lan => lan == 1))).Returns(Task.FromResult(countryResponse));

[Neu, als API noch Kopfzeilen hatte... Die Header wurden in ein anderes Wörterbuch des API-Aufrufers injiziert]

   this.mockedMasterAPICalls.Setup(m => m.RequestHeaders).Returns(new Dictionary<string, string>());
            this.mockedMasterAPICalls.Setup(m => m.GetCountries()).Returns(() =>
          {
              if (this.mockedMasterAPICalls.Object.RequestHeaders[GlobalConstants.HeaderOUInstance] == "2")
                  return Task.FromResult(countryResponse);
              else return Task.FromResult(new GetCountryResponse() { Countries = null });
          });

Beachten Sie die Verwendung des nachgebildeten Objekts selbst, um alle erforderlichen Entscheidungen zu treffen

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