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


169

이 수업을 상상해보십시오

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어떻게 확인할 수 있습니까?


"AsyncHandle"(추가 "n")을 의미 했습니까? 그리고 핸들러 코드를 게시하거나 표준 유형 인 경우 정규화 된 유형 이름을 지정할 수 있습니까?
TrueWill

당신의 생각을 보여주기 위해 골격 테스트를 보여줄 수 있습니까? 나는 당신 측에서 명백한 점을 알고 있지만, 우리 측에서는 오랜 투기 적 대답을하지 않고 질문에 대답 할 시간을 가지지 않은 사람처럼 보입니다.
Ruben Bartelink

1
Foo 나 Bar () 또는 이와 같은 것은 없습니다. 응용 프로그램 세부 사항을 방해하지 않고 현재 상황을 보여주는 데모 코드 일뿐입니다. 그리고 나는 단지 대답을 얻었습니다.
Jan

답변:


283

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

36
참고로 함수에 여러 개의 인수가있는 경우 일반 Callback<>()Moq 메소드 에서 모든 유형을 지정해야합니다 . 예를 들어, 메소드에 정의가있는 경우이 Handler.AnsyncHandle(string, SomeResponse)필요합니다 /* ... */.Callback<string, SomeResponse>(r => result = r);. 나는 이것을 여러 곳에서 명시 적으로 언급하지 않았으므로 여기에 추가 할 것이라고 생각했습니다.
Frank Bryce

12
@JavaJudt 답변을 보지 못한 경우 @Frank를 수정하고 싶었습니다. 이 개 인수를 얻을 수있는 제대로 방법은 다음과 같습니다/* ... */.Callback<string, SomeResponse>((s1, s2) => { str1 = s1; result = s2});
renatogbp

2
다음과 같이 람다 식에서 인수의 유형을 선언하면 동일한 결과를 얻을 수 있습니다..Callback((string s1, SomeResponse s2) => /* stuff */ )
jb637

1
내장 Capture.In헬퍼에 대한 참조를 포함하도록 응답을 업데이트 할 수 있습니까?
cao

29

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메소드에 다른 유형을 추가 하고 람다 식을 적절하게 변경하면됩니다.


22

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

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

21

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

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


15
알려진 매개 변수를 사용하여 메서드가 호출되었는지 확인하려는 경우이 방법이 좋습니다. 테스트 작성 시점에서 매개 변수가 아직 작성되지 않은 경우 (예 : 문제의 단위가 내부적으로 매개 변수를 작성하는 경우) 콜백을 사용하여이를 캡처하고 조사 할 수 있지만 접근 방식은 그렇지 않습니다.
Michael

1
전달 된 객체 집합이 모두 전달되었는지 확인해야하므로 전달 된 값을 저장해야합니다.
MrFox

또한 매개 변수가 엔터티 인 경우도 있으며 엔터티의 각 필드에 대해 별도의 어설 션이 필요합니다. 이를 수행하는 가장 좋은 방법은 Verify와 matcher를 사용하지 않고 param을 캡처하는 것입니다.
Kevin Wong

12

대안은의 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()

1
좋은 답변입니다! 이 답변을 볼 때까지 Moq.Capture 및 Moq.CaptureMatch 클래스를 알지 못했습니다. 캡처는 CallbackIMO 의 더 나은 대안 입니다. 매개 변수 목록에서 직접 캡처를 사용하기 때문에 메소드의 매개 변수 목록을 리팩토링 할 때 문제가 발생하기 어렵 기 때문에 테스트가 덜 취약합니다. 콜백을 사용하면 설정에서 전달 된 매개 변수를 콜백에 사용 된 유형 매개 변수와 동기화 된 상태로 유지해야하므로 과거에 문제가 발생했습니다.
저스틴 홀저

7

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

2

이것은 또한 작동합니다 :

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

2

여기에 좋은 답변이 많이 있습니다! 종속성에 전달 된 여러 클래스 매개 변수에 대한 어설 션을 작성해야 할 때까지 즉시 사용 가능한 Moq 기능 세트를 사용하십시오. 그 상황에서 Moq Verify 기능을 사용하면 Matcher는 테스트 실패를 격리하는 데 효과적이지 않으며 인수를 반환하는 콜백 / 반환 방식은 테스트에 불필요한 코드 줄을 추가합니다. 긴 테스트는 절망적입니다).

다음은 요지이다 https://gist.github.com/Jacob-McKay/8b8d41ebb9565f5fca23654fd944ac6b 나는 그 언급 한 단점없이, 모의 객체에 전달 된 인수에 대한 주장을 만들 수있는 더 선언적 방법을 제공 쓴 MOQ (4.12) 확장자가. 확인 섹션은 다음과 같습니다.

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

Moq가 선언적이며 실패 분리를 제공하면서 동일한 것을 달성하는 기능을 제공했다면 나는 좌절했을 것입니다. 손가락이 넘어졌다!


1
나는 이것을 좋아한다. Moq 확인은 어설 션 수행 권한에 대해 xUnit 어설 션과 경쟁합니다. 그것은 셋업의 Moq 부분에서 옳지 않다. It.Is 기능 설정은 비 표현식도 지원해야합니다.
토마스

"Moq는 어설 션 수행 권한을 위해 xUnit의 어설 션과 경쟁합니다"-잘 말했다. 나는 그것이 경쟁을 잃을 것이라고 덧붙였다. Moq는 일치하는 통화가 있었는지 알려주는 데 도움이되지만 특정 어설 션은 훨씬 나은 정보를 제공합니다. 내 접근 방식의 주요 단점은 유형 안전 및 매개 변수 순서 확인을 잃는 것입니다. 나는 이것에 대해 잠시 동안 개선을 찾고 있었지만, 함께 예제를 해킹 할 수있는 C # 닌자가 있습니다! 그렇지 않으면 내가 방법을 찾으면 이것을 업데이트 할 것입니다.
Jacob McKay
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.