Moq में मापदंडों को सौंपना / वापस लेना


293

क्या Moq (3.0+) का उपयोग करके out/ refपैरामीटर निर्दिष्ट करना संभव है ?

मैंने उपयोग करते हुए देखा है Callback(), लेकिन Action<>रेफरी मापदंडों का समर्थन नहीं करता क्योंकि यह जेनरिक पर आधारित है। मैं अधिमानतः पैरामीटर It.Isके इनपुट पर एक बाधा ( ) डालना चाहूंगा ref, हालांकि मैं कॉलबैक में ऐसा कर सकता हूं।

मुझे पता है कि राइनो मोक्स इस कार्यक्षमता का समर्थन करता है, लेकिन मैं जिस प्रोजेक्ट पर काम कर रहा हूं वह पहले से ही Moq का उपयोग कर रहा है।


4
यह Q & A Moq 3 के बारे में है। Moq 4.8 में री-अप मापदंडों के लिए समर्थन में बहुत सुधार हुआ है, जिसमें एक It.IsAny<T>()-समान मिलानकर्ता ( ref It.Ref<T>.IsAny) से लेकर सेट अप करने के लिए .Callback()और .Returns()कस्टम हस्ताक्षर प्रकार के माध्यम से समर्थन करने के लिए विधि हस्ताक्षर हैं। संरक्षित तरीके समान रूप से समर्थित हैं। नीचे मेरा जवाब उदा देखें ।
stakx -

आप इसका उपयोग कर सकते हैं। It.ef <TValue>। किसी भी विधि के लिए। जो पैरामीटर का उपयोग करता है। उदाहरण के लिए: moq.Setup (x => x.Method (It it .ef <string> .sAny)) .Rurnurns (TValue);
22

जवाबों:


118

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सी # 7 inमापदंडों के लिए भी काम करता है (क्योंकि वे भी रेफरी हैं)।


2
यह समाधान है, यह आपके पास रेफरी के रूप में कोई भी इनपुट है, ठीक उसी तरह जैसे कि यह नॉन रिफ इनपुट के लिए काम करेगा। यह एक बहुत ही अच्छा सुधार समर्थन वास्तव में है
grathad

5
यह एक ही समाधान के लिए काम नहीं करता है, outहालांकि यह करता है?
ATD

1
@ATD आंशिक रूप से हाँ। बाहर के पैरामीटर के साथ एक प्रतिनिधि की घोषणा करें और ऊपर से सिंटैक्स के साथ कॉलबैक में मान असाइन करें
RoyalTS

यह उल्लेख के लायक है कि यदि आप जिस फ़ंक्शन का मजाक उड़ा रहे हैं, उसके पास अधिक तर्क हैं, तो कॉलबैक हस्ताक्षर उसी पैटर्न का पालन करना चाहिए (न कि केवल रेफ / आउट पैरामीटर)
Yoav Feuerstein

320

'आउट' के लिए, निम्नलिखित मेरे लिए काम करता है।

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 'अपेक्षितवैल्यू' के मूल्य को देखता है।

के लिए ref, मैं भी एक जवाब के लिए देख रहा हूँ।

मुझे निम्न क्विकस्टार्ट गाइड उपयोगी लगी: https://github.com/Moq/moq4/wiki/Quickstart


7
मुझे लगता है कि मेरे पास जो समस्या थी, वह यह थी कि विधि से आउट / रेफरी पैरामेट्स को असाइन करने का कोई तरीका नहीं हैSetup
रिचर्ड सजेले

1
मेरे पास रेफरी पैरामीटर निर्दिष्ट करने के लिए कोई समाधान नहीं है। यह उदाहरण "आउटपुट वैल्यू" के मान को 'b' में निर्दिष्ट करता है। Moq आपके द्वारा सेट किए गए एक्सप्रेशन को निष्पादित नहीं करता है, यह इसका विश्लेषण करता है और यह महसूस करता है कि आप आउटपुट मूल्य के लिए 'a' प्रदान कर रहे हैं, इसलिए यह 'a' के वर्तमान मूल्य को देखता है और बाद की कॉल के लिए इसे याद रखता है।
क्रेग सेलेस्टे

बाहर भी देखें और उदाहरण देखें: code.google.com/p/moq/wiki/QuickStart
TrueWill

9
यह मेरे लिए काम नहीं करेगा जब मॉकड इंटरफ़ेस विधि को एक अलग दायरे में निष्पादित किया जाता है जिसका अपना संदर्भित आउटपुट चर होता है (उदाहरण के लिए किसी अन्य वर्ग की विधि के अंदर।) ऊपर दिया गया उदाहरण सुविधाजनक है क्योंकि निष्पादन उसी दायरे में होता है। नकली सेटअप, हालांकि यह सभी परिदृश्यों को हल करने के लिए बहुत सरल है। Moq में आउट / रेफ वैल्यू को स्पष्ट रूप से संभालने के लिए समर्थन (जैसा कि किसी और के द्वारा कहा गया है, निष्पादन समय पर नियंत्रित किया जाता है)।
जॉन के

2
+1: यह एक सहायक उत्तर है। लेकिन: यदि आउट पैरामीटर प्रकार एक वर्ग है, बल्कि एक बिल्ट-इन टाइप स्ट्रिंग जैसा है - मुझे विश्वास नहीं है कि यह काम करेगा। आज कोशिश की। मॉक ऑब्जेक्ट कॉल को सिमुलेट करता है और "आउट" पैरामीटर के माध्यम से एक नल देता है।
अज़ेगलोव

86

EDIT : Moq 4.10 में, आप अब एक प्रतिनिधि को पास कर सकते हैं जिसके पास कॉलबैक फ़ंक्शन के लिए एक आउट या रेफरी पैरामीटर है:

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

आपको एक प्रतिनिधि को परिभाषित करना होगा और उसे त्वरित करना होगा:

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

4.10 से पहले Moq संस्करण के लिए:

अवनर कश्तन अपने ब्लॉग में एक एक्सटेंशन विधि प्रदान करते हैं जो कॉलबैक से आउट पैरामीटर सेट करने की अनुमति देता है: Moq, कॉलबैक और आउट पैरामीटर: विशेष रूप से मुश्किल एज केस

समाधान सुरुचिपूर्ण और हैकी दोनों है। इसमें सुरुचिपूर्ण यह एक धाराप्रवाह सिंटैक्स प्रदान करता है जो अन्य Moq कॉलबैक के साथ घर पर महसूस करता है। और hacky क्योंकि यह प्रतिबिंब के माध्यम से कुछ आंतरिक 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);
    }



संपादित करें : शून्य-वापसी के तरीकों का समर्थन करने के लिए, आपको बस नए अधिभार तरीकों को जोड़ने की आवश्यकता है:

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

5
@Wilbert, मैंने अपना जवाब void-return फ़ंक्शन के लिए अतिरिक्त अधिभार के साथ अद्यतन किया है।
स्कॉट वेगनर

2
मैं अपने परीक्षण सूट में इस समाधान का उपयोग कर रहा हूं और काम कर रहा हूं। हालाँकि Moq 4.10 में अपडेट होने के बाद, यह अब नहीं चलता है।
रिस्तोगोड

2
ऐसा लगता है कि यह इस प्रतिबद्ध github.com/moq/moq4/commit/… में टूट गया था । शायद अब ऐसा करने का एक बेहतर तरीका है?
स्पार्कप्लग

1
Moq4 में fyi मेथडकॉल अब सेटअप की एक संपत्ति है, इसलिए OutCallbackInternal की हिम्मत ऊपर के बदलावों से होती हैvar methodCall = mock.GetType().GetProperty("Setup").GetValue(mock); mock.GetType().Assembly.GetType("Moq.MethodCall") .InvokeMember("SetCallbackResponse", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, methodCall, new[] { action });
mike

1
@Ristogod, Moq 4.10 के अपडेट के साथ, अब आप एक प्रतिनिधि को पास कर सकते हैं, जिसके पास कॉलबैक फ़ंक्शन के लिए एक आउट या रेफरी पैरामीटर है: mock.Setup(x=>x.Method(out d)).Callback(myDelegate).Returns(...);आपको एक प्रतिनिधि को परिभाषित करना होगा और उसे ...Callback(new MyDelegate((out decimal v)=>v=12m))...;
तुरंत भेजना

48

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

5
यह मूल रूप से पार्च्ड के उत्तर के समान है और इसमें एक ही सीमा है, इसमें यह इनपुट के आधार पर आउट मान को बदल नहीं सकता है और न ही यह पैरामीटर का जवाब दे सकता है।
रिचर्ड शेजले

@ रिचर्ड सोले, आप, लेकिन आपको अलग-अलग "आउटस्ट्रिंग" मापदंडों के साथ अलग-अलग सेटअप करने की आवश्यकता होगी
सिलु

17

ऐसा लगता है कि यह बॉक्स से बाहर संभव नहीं है। लगता है जैसे किसी ने समाधान का प्रयास किया

इस मंच पोस्ट को देखें http://code.google.com/p/moq/issues/detail?id=176

यह प्रश्न Moq के साथ संदर्भ पैरामीटर का मान सत्यापित करता है


पुष्टि के लिए धन्यवाद। मैंने वास्तव में अपनी खोज में उन दो लिंक को पाया था, लेकिन यह भी देखा कि Moq इसमें से एक को "सहायक रेफरी / आउट मापदंडों" के रूप में सूचीबद्ध करता है, इसलिए मैं निश्चित होना चाहता था।
रिचर्ड शालय जूल

3

रेफरी पैरामीटर सेट करने के साथ एक मूल्य वापस करने के लिए, यहाँ एक कोड है:

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

क्या यह काम तब होता है जब आपके पास उस चर की पहुँच नहीं होती है जो y के रूप में पारित होने वाली है? मेरे पास एक फ़ंक्शन है जो DayOfWeek पर दो रेफरी तर्क देता है। मुझे इन दोनों को मॉक स्टब में एक विशेष दिन पर सेट करने की आवश्यकता है और तीसरा तर्क एक मॉक डेटाबेस संदर्भ है। लेकिन प्रतिनिधि पद्धति को अभी नहीं बुलाया जा रहा है। ऐसा लगता है कि Moq स्थानीय "y" MyMethod "फ़ंक्शन से पारित होने की उम्मीद कर रहा होगा। क्या यह आपके उदाहरण के लिए काम करता है? धन्यवाद।
ग्रेग वेर

3

बिली जेक्स अवेयरर पर बिल्डिंग, मैंने एक आउट पैरामीटर के साथ पूरी तरह से गतिशील मॉक विधि बनाई। मैं इसे यहाँ पोस्ट कर रहा हूँ जो किसी के लिए इसे उपयोगी पाता है (शायद सिर्फ भविष्य में)।

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

2

मुझे यकीन है कि स्कॉट का समाधान एक बिंदु पर काम करता है,

लेकिन यह निजी एपिस में चित्रण का उपयोग न करने के लिए एक अच्छा तर्क है। अब टूट चुका है।

मैं एक प्रतिनिधि का उपयोग करके पैरामीटर सेट करने में सक्षम था

      delegate void MockOutDelegate(string s, out int value);

    public void SomeMethod()
    {
        ....

         int value;
         myMock.Setup(x => x.TryDoSomething(It.IsAny<string>(), out value))
            .Callback(new MockOutDelegate((string s, out int output) => output = userId))
            .Returns(true);
    }

1

यह एक समाधान हो सकता है।

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

}

1
यह मूल रूप से पार्च्ड के उत्तर के समान है और इसमें एक ही सीमा है, इसमें यह इनपुट के आधार पर आउट मान को बदल नहीं सकता है और न ही यह पैरामीटर का जवाब दे सकता है।
रिचर्ड शेजय

1

इससे पहले कि मैं सरल एक नए 'नकली' वर्ग का एक उदाहरण बनाया है कि जो भी इंटरफ़ेस आप मॉक करने के लिए कोशिश कर रहे हैं लागू करता है कि मैं यहाँ कई सुझावों के साथ संघर्ष किया। फिर आप केवल विधि के साथ आउट पैरामीटर का मान सेट कर सकते हैं।


0

मैं आज दोपहर एक घंटे तक इससे जूझता रहा और कहीं भी इसका जवाब नहीं मिला। इसके साथ अपने आसपास खेलने के बाद मैं एक समाधान के साथ आने में सक्षम था जो मेरे लिए काम करता था।

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

यहाँ कुंजी है mock.SetupAllProperties();जो आपके लिए सभी गुणों को बाहर निकाल देगी। यह हर परीक्षण स्थिति में काम नहीं कर सकता है, लेकिन सभी करता है, तो आपके लिए महत्वपूर्ण हैं हो रही है return valueकी YourMethodतो इस वसीयत काम ठीक।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.