webentwicklung-frage-antwort-db.com.de

Zuweisen von / Ref-Parametern in Moq

Ist es möglich, einen out/ref -Parameter mit Moq (3.0+) zuzuweisen?

Ich habe mit Callback() gesucht, aber Action<> unterstützt keine ref-Parameter, da es auf Generika basiert. Ich möchte auch vorzugsweise eine Einschränkung setzen (It.Is) über die Eingabe des Parameters ref, obwohl ich das im Callback tun kann.

Ich weiß, dass Rhino Mocks diese Funktionalität unterstützt, aber das Projekt, an dem ich arbeite, verwendet bereits Moq.

254
Richard Szalay

Während es sich bei der Frage um Moq 3 handelt (wahrscheinlich aufgrund seines Alters), lassen Sie mich eine Lösung für Moq 4.8 veröffentlichen, die die Unterstützung für By-Ref-Parameter erheblich verbessert hat.

public interface IGobbler
{
    bool Gobble(ref int amount);
}

delegate void GobbleCallback(ref int amount);     // needed for Callback
delegate bool GobbleReturns(ref int amount);      // needed for Returns

var mock = new Mock<IGobbler>();
mock.Setup(m => m.Gobble(ref It.Ref<int>.IsAny))  // match any value passed by-ref
    .Callback(new GobbleCallback((ref int amount) =>
     {
         if (amount > 0)
         {
             Console.WriteLine("Gobbling...");
             amount -= 1;
         }
     }))
    .Returns(new GobbleReturns((ref int amount) => amount > 0));

int a = 5;
bool gobbleSomeMore = true;
while (gobbleSomeMore)
{
    gobbleSomeMore = mock.Object.Gobble(ref a);
}

Apropos: It.Ref<T>.IsAny funktioniert auch für C # 7 in -Parameter (da diese ebenfalls by-ref sind).

76
stakx

Für 'out' scheint das Folgende für mich zu funktionieren.

public interface IService
{
    void DoSomething(out string a);
}

[TestMethod]
public void Test()
{
    var service = new Mock<IService>();
    var expectedValue = "value";
    service.Setup(s => s.DoSomething(out expectedValue));

    string actualValue;
    service.Object.DoSomething(out actualValue);
    Assert.AreEqual(expectedValue, actualValue);
}

Ich vermute, dass Moq beim Aufrufen von Setup den Wert von 'expectedValue' überprüft und sich daran erinnert.

Für ref suche ich auch eine Antwort.

Ich fand die folgende Kurzanleitung nützlich: https://github.com/Moq/moq4/wiki/Quickstart

296
Craig Celeste

[~ # ~] edit [~ # ~] : In Moq 4.10 können Sie jetzt einen Delegaten mit einem out- oder ref-Parameter direkt an den übergeben Rückruffunktion:

mock
  .Setup(x=>x.Method(out d))
  .Callback(myDelegate)
  .Returns(...); 

Sie müssen einen Delegaten definieren und instanziieren:

...
.Callback(new MyDelegate((out decimal v)=>v=12m))
...

Für Moq-Version vor 4.10:

Avner Kashtan stellt in seinem Blog eine Erweiterungsmethode zur Verfügung, mit der der out-Parameter eines Callbacks festgelegt werden kann: Moq-, Callbacks- und Out-Parameter: ein besonders schwieriger Edge-Fall

Die Lösung ist sowohl elegant als auch kitschig. Elegant dahingehend, dass es eine flüssige Syntax bietet, die sich bei anderen Moq-Rückrufen wohlfühlt. Und hacky, weil es auf dem Aufruf einiger interner Moq-APIs über Reflection beruht.

Die unter dem obigen Link angegebene Erweiterungsmethode wurde für mich nicht kompiliert, daher habe ich unten eine bearbeitete Version bereitgestellt. Sie müssen für jede Anzahl von Eingabeparametern eine Signatur erstellen. Ich habe 0 und 1 angegeben, aber die Erweiterung sollte einfach sein:

public static class MoqExtensions
{
    public delegate void OutAction<TOut>(out TOut outVal);
    public delegate void OutAction<in T1,TOut>(T1 arg1, out TOut outVal);

    public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, TOut>(this ICallback<TMock, TReturn> mock, OutAction<TOut> action)
        where TMock : class
    {
        return OutCallbackInternal(mock, action);
    }

    public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, T1, TOut>(this ICallback<TMock, TReturn> mock, OutAction<T1, TOut> action)
        where TMock : class
    {
        return OutCallbackInternal(mock, action);
    }

    private static IReturnsThrows<TMock, TReturn> OutCallbackInternal<TMock, TReturn>(ICallback<TMock, TReturn> mock, object action)
        where TMock : class
    {
        mock.GetType()
            .Assembly.GetType("Moq.MethodCall")
            .InvokeMember("SetCallbackWithArguments", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock,
                new[] { action });
        return mock as IReturnsThrows<TMock, TReturn>;
    }
}

Mit der obigen Erweiterungsmethode können Sie eine Schnittstelle mit folgenden Parametern testen:

public interface IParser
{
    bool TryParse(string token, out int value);
}

.. mit folgendem Moq-Setup:

    [TestMethod]
    public void ParserTest()
    {
        Mock<IParser> parserMock = new Mock<IParser>();

        int outVal;
        parserMock
            .Setup(p => p.TryParse("6", out outVal))
            .OutCallback((string t, out int v) => v = 6)
            .Returns(true);

        int actualValue;
        bool ret = parserMock.Object.TryParse("6", out actualValue);

        Assert.IsTrue(ret);
        Assert.AreEqual(6, actualValue);
    }



Bearbeiten : Zur Unterstützung von Void-Return-Methoden müssen Sie einfach neue Überladungsmethoden hinzufügen:

public static ICallbackResult OutCallback<TOut>(this ICallback mock, OutAction<TOut> action)
{
    return OutCallbackInternal(mock, action);
}

public static ICallbackResult OutCallback<T1, TOut>(this ICallback mock, OutAction<T1, TOut> action)
{
    return OutCallbackInternal(mock, action);
}

private static ICallbackResult OutCallbackInternal(ICallback mock, object action)
{
    mock.GetType().Assembly.GetType("Moq.MethodCall")
        .InvokeMember("SetCallbackWithArguments", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock, new[] { action });
    return (ICallbackResult)mock;
}

Dies ermöglicht das Testen von Schnittstellen wie:

public interface IValidationRule
{
    void Validate(string input, out string message);
}

[TestMethod]
public void ValidatorTest()
{
    Mock<IValidationRule> validatorMock = new Mock<IValidationRule>();

    string outMessage;
    validatorMock
        .Setup(v => v.Validate("input", out outMessage))
        .OutCallback((string i, out string m) => m  = "success");

    string actualMessage;
    validatorMock.Object.Validate("input", out actualMessage);

    Assert.AreEqual("success", actualMessage);
}
80
Scott Wegner

Dies ist die Dokumentation von Moq-Site :

// out arguments
var outString = "ack";
// TryParse will return true, and the out argument will return "ack", lazy evaluated
mock.Setup(foo => foo.TryParse("ping", out outString)).Returns(true);


// ref arguments
var instance = new Bar();
// Only matches if the ref argument to the invocation is the same instance
mock.Setup(foo => foo.Submit(ref instance)).Returns(true);
47
Kosau

Scheint so, als ob es nicht sofort möglich ist. Anscheinend hat jemand versucht, eine Lösung zu finden

Siehe diesen Forumsbeitrag http://code.google.com/p/moq/issues/detail?id=176

diese Frage Überprüfen Sie den Wert des Referenzparameters mit Moq

17
Gishu

Um einen Wert zusammen mit dem Setzen des ref-Parameters zurückzugeben, ist hier ein Teil des Codes:

public static class MoqExtensions
{
    public static IReturnsResult<TMock> DelegateReturns<TMock, TReturn, T>(this IReturnsThrows<TMock, TReturn> mock, T func) where T : class
        where TMock : class
    {
        mock.GetType().Assembly.GetType("Moq.MethodCallReturn`2").MakeGenericType(typeof(TMock), typeof(TReturn))
            .InvokeMember("SetReturnDelegate", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock,
                new[] { func });
        return (IReturnsResult<TMock>)mock;
    }
}

Deklarieren Sie dann Ihren eigenen Delegaten, der der Signatur der zu verspottenden Methode entspricht, und stellen Sie Ihre eigene Methodenimplementierung bereit.

public delegate int MyMethodDelegate(int x, ref int y);

    [TestMethod]
    public void TestSomething()
    {
        //Arrange
        var mock = new Mock<ISomeInterface>();
        var y = 0;
        mock.Setup(m => m.MyMethod(It.IsAny<int>(), ref y))
        .DelegateReturns((MyMethodDelegate)((int x, ref int y)=>
         {
            y = 1;
            return 2;
         }));
    }
2

Dies kann eine Lösung sein.

[Test]
public void TestForOutParameterInMoq()
{
  //Arrange
  _mockParameterManager= new Mock<IParameterManager>();

  Mock<IParameter > mockParameter= new Mock<IParameter >();
  //Parameter affectation should be useless but is not. It's really used by Moq 
  IParameter parameter= mockParameter.Object;

  //Mock method used in UpperParameterManager
  _mockParameterManager.Setup(x => x.OutMethod(out parameter));

  //Act with the real instance
  _UpperParameterManager.UpperOutMethod(out parameter);

  //Assert that method used on the out parameter of inner out method are really called
  mockParameter.Verify(x => x.FunctionCalledInOutMethodAfterInnerOutMethod(),Times.Once());

}
1
Fabrice

Ich hatte Probleme mit vielen der hier gemachten Vorschläge, bevor ich einfach eine Instanz einer neuen 'Fake'-Klasse erstellte, die die Schnittstelle implementiert, die Sie zu verspotten versuchen. Dann können Sie einfach den Wert des out-Parameters mit der Methode selbst einstellen.

1
Casey O'Brien

Ich hatte heute Nachmittag eine Stunde damit zu kämpfen und konnte nirgendwo eine Antwort finden. Nachdem ich alleine herumgespielt hatte, konnte ich eine Lösung finden, die für mich funktionierte.

string firstOutParam = "first out parameter string";
string secondOutParam = 100;
mock.SetupAllProperties();
mock.Setup(m=>m.Method(out firstOutParam, out secondOutParam)).Returns(value);

Der Schlüssel hier ist mock.SetupAllProperties();, wodurch alle Eigenschaften für Sie gelöscht werden. Dies funktioniert möglicherweise nicht in jedem Testfallszenario, aber wenn Sie nur den return value Von YourMethod erhalten möchten, funktioniert dies problemlos.

0
maxshuty