Question

Est-il possible d'assigner un out / paramètre ref à l'aide Moq (3.0 +)?

Je l'ai regardé en utilisant Callback(), mais ne supporte pas Action<> paramètres ref parce qu'il est basé sur les génériques. Je voudrais aussi mettre de préférence une contrainte (It.Is) sur l'entrée du paramètre <=>, bien que je peux le faire dans le rappel.

Je sais que Rhino Mocks prend en charge cette fonctionnalité, mais le projet je travaille utilise déjà Moq.

Était-ce utile?

La solution

Alors que la question porte sur Moq 3 (probablement en raison de son âge), permettez-moi de poster une solution pour Moq 4,8, ce qui a beaucoup amélioré le soutien pour les paramètres par-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);
}

Par ailleurs:. It.Ref<T>.IsAny travaille également pour C # 7 paramètres in (car ils sont aussi par-ref)

Autres conseils

Pour 'out', ce qui suit semble fonctionner pour moi.

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

Je devine que Moq regarde la valeur de « expectedValue » lorsque vous appelez le programme d'installation et il se souvient.

Pour ref, je suis à la recherche d'une réponse aussi.

J'ai trouvé le guide de démarrage rapide suivant utile: https://github.com/Moq/moq4/wiki/Quickstart

EDIT : Dans Moq 4.10, vous pouvez maintenant passer un délégué qui a un paramètre ou sur ref directement à la fonction de rappel:

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

Vous devrez définir un délégué et instancier:

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

Pour la version 4.10 avant Moq:

Avner Kashtan fournit une méthode d'extension dans son blog qui permet de régler le paramètre à partir d'un rappel: Moq, les paramètres Callbacks et de sortie: un boîtier de bord particulièrement délicat

La solution est à la fois élégante et hacky. Élégant en ce qu'elle fournit une syntaxe couramment qui se sent à la maison avec d'autres callbacks moq. Et hacky car il repose sur l'appel des API moq internes par réflexion.

La méthode d'extension fournie sur le lien ci-dessus ne compile pas pour moi, donc j'ai fourni une version modifiée ci-dessous. Vous aurez besoin de créer une signature pour chaque nombre de paramètres d'entrée que vous avez; J'ai fourni 0 et 1, mais l'étendre plus doit être simple:

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

Avec la méthode d'extension ci-dessus, vous pouvez tester une interface avec des paramètres tels que sur:

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

.. avec la configuration Moq suivante:

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


Modifier : Pour soutenir les méthodes vides retour, il vous suffit d'ajouter de nouvelles méthodes de surcharge:

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

Cela permet à des interfaces de test telles que:

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

Cette documentation est de Site Moq :

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

On dirait qu'il est impossible de la boîte. On dirait que quelqu'un a tenté une solution

Voir ce post forum http://code.google.com/p/moq/issues/ détail? id = 176

cette question vérifier la valeur de paramètre de référence avec Moq

Pour revenir une valeur avec réglage des paramètres ref, voici un morceau de code:

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

Ensuite, déclarer votre propre délégué correspondant à la signature de la méthode à être moqué-et fournir votre propre implémentation de la méthode.

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

Cela peut être une solution.

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

}

Je débattais avec plusieurs des suggestions ici avant simple, créé une instance d'une nouvelle classe « faux » qui implémente l'interface quelle que soit vous essayez de Mock dehors. Ensuite, vous pouvez simplement définir la valeur du paramètre avec la méthode elle-même.

Je débattais avec cela pendant une heure cet après-midi et ne pouvait pas trouver une réponse nulle part. Après avoir joué moi-même avec elle, je suis en mesure de trouver une solution qui a fonctionné pour moi.

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

La clé ici est mock.SetupAllProperties(); qui bouchonner toutes les propriétés pour vous. Cela peut ne pas fonctionner dans tous les scénarios de cas de test, mais si tout ce que vous soucier de l'obtenir est de return value alors cela YourMethod fonctionnera très bien.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top