development

Moq : 모의 서비스의 메소드에 전달 된 매개 변수를 얻는 방법

big-blog 2020. 6. 15. 07:49
반응형

Moq : 모의 서비스의 메소드에 전달 된 매개 변수를 얻는 방법


이 수업을 상상해보십시오

public class Foo {

    private Handler _h;

    public Foo(Handler h)
    {
        _h = h;
    }

    public void Bar(int i)
    {
        _h.AsyncHandle(CalcOn(i));
    }

    private SomeResponse CalcOn(int i)
    {
        ...;
    }
}

Foo 테스트에서 Mo (q) cking Handler, Bar()전달 된 내용을 _h.AsyncHandle어떻게 확인할 수 있습니까?


Mock.Callback-method를 사용할 수 있습니다 :

var mock = new Mock<Handler>();
SomeResponse result = null;
mock.Setup(h => h.AnsyncHandle(It.IsAny<SomeResponse>()))
    .Callback<SomeResponse>(r => result = r);

// do your test
new Foo(mock.Object).Bar(22);
Assert.NotNull(result);

전달 된 인수에서 간단한 것을 확인하려면 직접 할 수도 있습니다.

mock.Setup(h => h.AnsyncHandle(It.Is<SomeResponse>(response => response != null)));

Gamlor의 답변은 효과가 있지만 그것을하는 또 다른 방법 (및 테스트에서 더 표현력이 있다고 생각되는 방법)은 ...

var mock = new Mock<Handler>();
var desiredParam = 47; // this is what you want to be passed to AsyncHandle
new Foo(mock.Object).Bar(22);
mock.Verify(h => h.AsyncHandle(desiredParam), Times.Once());

확인은 매우 강력하며 익숙해지기 위해 시간을 할애 할 가치가 있습니다.


Gamlor의 대답은 저에게 효과적이지만 John Carpenter의 의견을 확장 할 것이라고 생각했습니다. 하나 이상의 매개 변수와 관련된 솔루션을 찾고 있었기 때문입니다. 이 페이지를 우연히 본 다른 사람들도 비슷한 상황에 처해있을 것이라고 생각했습니다. Moq documentation 에서이 정보를 찾았습니다 .

Gamlor의 예제를 사용하지만 AsyncHandle 메서드는 a stringSomeResponseobject 라는 두 가지 인수를 취하는 것처럼 가장 합니다.

var mock = new Mock<Handler>();
string stringResult = string.Empty;
SomeResponse someResponse = null;
mock.Setup(h => h.AsyncHandle(It.IsAny<string>(), It.IsAny<SomeResponse>()))
    .Callback<string, SomeResponse>((s, r) => 
    {
        stringResult = s;
        someResponse = r;
    });

// do your test
new Foo(mock.Object).Bar(22);
Assert.AreEqual("expected string", stringResult);
Assert.IsNotNull(someResponse);

기본적으로 It.IsAny<>()적절한 유형의 다른 유형을 추가하고 Callback메소드에 다른 유형을 추가 하고 람다 식을 적절하게 변경하면됩니다.


콜백 메소드는 확실히 작동하지만 많은 매개 변수가있는 메소드 에서이 작업을 수행하는 경우 약간 장황 할 수 있습니다. 다음은 상용구를 제거하는 데 사용한 것입니다.

var mock = new Mock<Handler>();

// do your test   
new Foo(mock.Object).Bar(22);

var arg = new ArgumentCaptor<SomeResponse>();
mock.Verify(h => h.AsyncHandle(arg.Capture()));
Assert.NotNull(arg.Value);

ArgumentCaptor의 소스는 다음과 같습니다.

public class ArgumentCaptor<T>
{
    public T Capture()
    {
        return It.Is<T>(t => SaveValue(t));
    }

    private bool SaveValue(T t)
    {
        Value = t;
        return true;
    }

    public T Value { get; private set; }
}

It.Is<TValue>()매처를 사용할 수 있습니다 .

var mock = new Mock<Handler>();
new Foo(mock.Object).Bar(22);
mock.Verify(h => h.AsyncHandle(It.Is<SomeResponse>(r => r != null )));

대안은의 Capture.In기능 을 사용 하는 것 moq입니다. moq컬렉션에서 인수 캡처를 가능하게하는 것은 OOTB 기능입니다.

//Arrange
var args = new List<SomeResponse>();
mock.Setup(h => h.AnsyncHandle(Capture.In(args)));

//Act
new Foo(mock.Object).Bar(22);

//Assert
//... assert args.Single() or args.First()

이것은 또한 작동합니다 :

Mock<InterfaceThing> mockedObject = new Mock<InterfaceThing>();
var objectParameter = mockedObject.Invocations[1].Arguments[0] as ObjectParameter;

Lot's of good answers here! Go with the out of the box Moq feature-set until you need to make assertions about several class parameters passed to your dependencies. If you end up in that situation though, the Moq Verify feature with It.Is matchers doesn't do a good job of isolating the test failure, and the Returns/Callback way of capturing arguments adds unnecessary lines of code to your test (and long tests are a no-go for me).

Here is a gist: https://gist.github.com/Jacob-McKay/8b8d41ebb9565f5fca23654fd944ac6b with a Moq (4.12) extension I wrote that gives a more declarative way to make assertions about arguments passed to mocks, without the drawbacks aforementioned. Here is what the Verify section looks like now:

        mockDependency
            .CheckMethodWasCalledOnce(nameof(IExampleDependency.PersistThings))
            .WithArg<InThing2>(inThing2 =>
            {
                Assert.Equal("Input Data with Important additional data", inThing2.Prop1);
                Assert.Equal("I need a trim", inThing2.Prop2);
            })
            .AndArg<InThing3>(inThing3 =>
            {
                Assert.Equal("Important Default Value", inThing3.Prop1);
                Assert.Equal("I NEED TO BE UPPER CASED", inThing3.Prop2);
            });

I would be stoked if Moq provided a feature that accomplished the same thing while being as declarative and providing the failure isolation this does. Fingers crossed!

참고URL : https://stackoverflow.com/questions/3269717/moq-how-to-get-to-a-parameter-passed-to-a-method-of-a-mocked-service

반응형