Domanda

E 'possibile assegnare un parametro out / ref utilizzando Moq (3.0 +)?

Ho guardato utilizzando Callback(), ma Action<> non supporta parametri ref, perché si basa su farmaci generici. Mi piacerebbe anche piace preferibilmente a mettere un vincolo (It.Is) sull'ingresso del <=> parametri, anche se posso farlo nel callback.

So che Rhino Mocks supporta questa funzionalità, ma il progetto a cui sto lavorando è già in uso Moq.

È stato utile?

Soluzione

Mentre la questione è di circa Moq 3 (probabilmente a causa della sua età), mi permette di postare una soluzione per Moq 4.8, che ha molto migliorato il supporto per i parametri di by-ref.

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);
}

A proposito:. It.Ref<T>.IsAny funziona anche per C # 7 in parametri (dal momento che sono anche da-ref)

Altri suggerimenti

Per 'out', la seguente sembra funzionare per me.

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);
}

Sto indovinando che Moq guarda al valore di 'expectedValue' quando si chiama il programma di installazione e lo ricorda.

Per ref, sto cercando una risposta anche.

ho trovato la seguente guida QuickStart utili: https://github.com/Moq/moq4/wiki/Quickstart

Modifica : In Moq 4.10, è ora possibile passare un delegato che ha un fuori o un parametro ref direttamente alla funzione di callback:

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

Si dovrà definire un delegato e un'istanza:

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

Per la versione Moq prima di 4.10:

Avner Kashtan fornisce un metodo di estensione nel suo blog che permette di impostare il parametro fuori da una richiamata: Moq, callback e out parametri: un caso particolarmente difficile bordo

La soluzione è allo stesso tempo elegante e hacky. Elegante in quanto fornisce una sintassi fluente che si sente a casa con altri callback Moq. E hacky perché si basa sulla chiamando alcune API interne Moq attraverso la riflessione.

Il metodo di estensione fornito al link qui sopra non ha compilato per me, così ho ottenere una versione adattata di seguito. Avrai bisogno di creare una firma per ogni numero di parametri di input si dispone; Ho fornito 0 e 1, ma si estende ulteriormente dovrebbe essere semplice:

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>;
    }
}

Con il metodo di estensione di cui sopra, è possibile verificare un'interfaccia con i parametri fuori come ad esempio:

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

.. con la seguente configurazione Moq:

    [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);
    }


Modifica : Per supportare i metodi vuoto-ritorno, è sufficiente aggiungere nuovi metodi di sovraccarico:

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;
}

In questo modo le interfacce di test come ad esempio:

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);
}

Questa è la documentazione da Moq sito :

// 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);

Sembra che non sia possibile, fuori dalla scatola. Sembra che qualcuno ha tentato una soluzione

Vedi questo post sul forum http://code.google.com/p/moq/issues/ dettaglio? id = 176

questa domanda Verificare il valore del parametro di riferimento con Moq

Per restituire un valore lungo con l'impostazione dei parametri ref, qui è un pezzo di codice:

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;
    }
}

Poi dichiarare il proprio delegato corrispondente alla firma del metodo per-essere-deriso e fornire la propria implementazione del metodo.

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;
         }));
    }

Questa può essere una soluzione.

[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());

}

ho lottato con molti dei suggerimenti qui prima che semplice creato un'istanza di una nuova classe 'falso' che implementa qualsiasi interfaccia che si sta cercando di deridere fuori. Poi si può semplicemente impostare il valore del parametro con il metodo stesso.

Ho lottato con questo per un'ora questo pomeriggio e non sono riuscito a trovare una risposta da nessuna parte. Dopo aver suonato in giro per conto mio con lui sono stato in grado di trovare una soluzione che ha funzionato per me.

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

La chiave qui è mock.SetupAllProperties(); che spegnere tutte le proprietà per voi. Questo potrebbe non funzionare in tutti i casi scenario di test, ma se tutto quello che interessa è ottenere la return value di YourMethod allora questo funzionerà bene.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top