[C#] Moq에서 out / ref 파라미터 지정

Moq (3.0+)를 사용하여 out/ ref매개 변수 를 할당 할 수 있습니까?

내가 사용을 검토 한 결과 Callback(),하지만 Action<>이 제네릭을 기반으로하기 때문에 심판이 매개 변수를 지원하지 않습니다. 콜백에서이를 수행 할 수는 있지만 매개 변수 It.Is의 입력에 제약 조건 ( ) 을 넣고 싶습니다 ref.

Rhino Mocks가이 기능을 지원한다는 것을 알고 있지만 현재 작업중인 프로젝트는 이미 Moq를 사용하고 있습니다.



답변

Moq 버전 4.8 (이상)은 참조 매개 변수에 대한 지원이 크게 향상되었습니다.

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

out매개 변수에 대해 동일한 패턴이 작동합니다 .

It.Ref<T>.IsAny또한 C # 7 in매개 변수에 대해서도 작동합니다 (참조 매개 변수이기 때문에).


답변

‘아웃’의 경우 다음이 저에게 효과적입니다.

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

설치 프로그램을 호출하고 기억할 때 Moq가 ‘expectedValue’의 값을 보는 것으로 추측합니다.

에 대한 ref답변도 찾고 있습니다.

다음 빠른 시작 안내서가 유용하다는 것을 알았습니다.
https://github.com/Moq/moq4/wiki/Quickstart


답변

편집 : Moq 4.10에서는 out 또는 ref 매개 변수가있는 델리게이트를 콜백 함수에 직접 전달할 수 있습니다.

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

델리게이트를 정의하고 인스턴스화해야합니다.

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

4.10 이전의 Moq 버전 :

Avner Kashtan은 블로그 에서 콜백에서 출력 매개 변수 (Moq, Callbacks 및 Out 매개 변수 : 특히 까다로운 경우)를 설정할 수있는 확장 방법을 제공합니다 .

이 솔루션은 우아하고 해킹 적입니다. 다른 Moq 콜백과 함께 집처럼 느껴지는 유창한 구문을 제공한다는 점에서 우아합니다. 그리고 해키는 리플렉션을 통해 내부 Moq API를 호출하는 데 의존하기 때문에 해키입니다.

위의 링크에서 제공되는 확장 방법은 나를 위해 컴파일되지 않았으므로 아래에서 편집 된 버전을 제공했습니다. 보유한 각 입력 매개 변수 수에 대한 서명을 작성해야합니다. 나는 0과 1을 제공했지만 더 확장하는 것은 간단해야합니다.

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

위의 확장 방법을 사용하면 다음과 같은 매개 변수없이 인터페이스를 테스트 할 수 있습니다.

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

.. 다음 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);
    }

편집 : void-return 메소드를 지원하려면 새 과부하 메소드를 추가하기 만하면됩니다.

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

이를 통해 다음과 같은 인터페이스 테스트가 가능합니다.

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


답변

이것은 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);


답변

상자 밖으로는 불가능한 것 같습니다. 누군가가 해결책을 시도한 것처럼 보입니다.

이 포럼 게시물을 참조하십시오
http://code.google.com/p/moq/issues/detail?id=176

이 질문
Moq로 참조 매개 변수의 값을 확인하십시오


답변

ref 매개 변수 설정과 함께 값을 리턴하려면 다음 코드를 작성하십시오.

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

그런 다음 조롱 할 메소드의 서명과 일치하는 자신의 델리게이트를 선언하고 자신의 메소드 구현을 제공하십시오.

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


답변

Billy Jakes awnser를 기반으로 out 매개 변수를 사용하여 완전히 역동적 인 mock 메소드를 만들었습니다. 나는 이것이 유용하다고 생각하는 사람 (아마도 미래에 나에게 있음)을 위해 이것을 여기에 게시하고 있습니다.

// Define a delegate with the params of the method that returns void.
delegate void methodDelegate(int x, out string output);

// Define a variable to store the return value.
bool returnValue;

// Mock the method: 
// Do all logic in .Callback and store the return value.
// Then return the return value in the .Returns
mockHighlighter.Setup(h => h.SomeMethod(It.IsAny<int>(), out It.Ref<int>.IsAny))
  .Callback(new methodDelegate((int x, out int output) =>
  {
    // do some logic to set the output and return value.
    output = ...
    returnValue = ...
  }))
  .Returns(() => returnValue);