Moq के साथ एक विशिष्ट पैरामीटर का सत्यापन


170
public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully()
{
    var messageServiceClientMock = new Mock<IMessageServiceClient>();
    var queueableMessage = CreateSingleQueueableMessage();
    var message = queueableMessage[0];
    var xml = QueueableMessageAsXml(queueableMessage);
    messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(xml)).Verifiable();
    //messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny<XmlElement>())).Verifiable();

    var serviceProxyFactoryStub = new Mock<IMessageServiceClientFactory>();
    serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(essageServiceClientMock.Object);
    var loggerStub = new Mock<ILogger>();

    var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object);
    client.SubmitMessagesToQueue(new List<IMessageRequestDTO> {message});

    //messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(xml), Times.Once());
    messageServiceClientMock.Verify();
}

मैं Moq का उपयोग करना शुरू कर रहा हूं और थोड़ा संघर्ष कर रहा हूं। मैं यह सत्यापित करने की कोशिश कर रहा हूं कि मैसेज सर्विस क्लाइंट को सही पैरामीटर प्राप्त हो रहा है, जो एक XmlElement है, लेकिन मुझे इसे काम करने का कोई तरीका नहीं मिल रहा है। यह तभी काम करता है जब मैं किसी विशेष मूल्य की जांच नहीं करता।

कोई विचार?

आंशिक उत्तर: मुझे यह जांचने का एक तरीका मिल गया है कि प्रॉक्सी को भेजे गए xml सही हैं, लेकिन मुझे अभी भी नहीं लगता कि यह करने का सही तरीका है।

public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully()
{
    var messageServiceClientMock = new Mock<IMessageServiceClient>();
    messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny<XmlElement>())).Verifiable();
    var serviceProxyFactoryStub = new Mock<IMessageServiceClientFactory>();
    serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(messageServiceClientMock.Object);
    var loggerStub = new Mock<ILogger>();

    var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object);
    var message = CreateMessage();
    client.SubmitMessagesToQueue(new List<IMessageRequestDTO> {message});

    messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(It.Is<XmlElement>(xmlElement => XMLDeserializer<QueueableMessage>.Deserialize(xmlElement).Messages.Contains(message))), Times.Once());
}

वैसे, मैं Verify कॉल से एक्सप्रेशन कैसे निकाल सकता हूं?

जवाबों:


251

यदि सत्यापन तर्क गैर-तुच्छ है, तो एक बड़े लैंबडा विधि (जैसा कि आपका उदाहरण दिखाता है) लिखना गड़बड़ होगा। आप सभी परीक्षण कथनों को एक अलग विधि में रख सकते हैं, लेकिन मुझे यह पसंद नहीं है क्योंकि यह परीक्षण कोड पढ़ने के प्रवाह को बाधित करता है।

एक अन्य विकल्प सेटअप कॉल पर एक कॉलबैक का उपयोग करना है जो उस मूल्य को संग्रहीत करने के लिए है जिसे नकली विधि में पारित किया गया था, और फिर Assertइसे मान्य करने के लिए मानक तरीके लिखें । उदाहरण के लिए:

// Arrange
MyObject saveObject;
mock.Setup(c => c.Method(It.IsAny<int>(), It.IsAny<MyObject>()))
        .Callback<int, MyObject>((i, obj) => saveObject = obj)
        .Returns("xyzzy");

// Act
// ...

// Assert
// Verify Method was called once only
mock.Verify(c => c.Method(It.IsAny<int>(), It.IsAny<MyObject>()), Times.Once());
// Assert about saveObject
Assert.That(saveObject.TheProperty, Is.EqualTo(2));

6
इस दृष्टिकोण का एक बड़ा लाभ यह है कि यह आपको विशिष्ट परीक्षण विफलता देगा कि वस्तु कैसे गलत है (जैसा कि आप प्रत्येक व्यक्ति का परीक्षण कर रहे हैं)।
रोब चर्च

1
मैंने सोचा था कि मैं केवल एक ही था जिसने ऐसा किया, यह देखने के लिए खुशी है कि यह एक उचित दृष्टिकोण है!
विल Appleby

3
मैं It.Is <MyObject> (सत्यापनकर्ता) का उपयोग कर के अनुसार मेयो बेहतर है के रूप में यह लैम्ब्डा के हिस्से के रूप पैरामीटर मान बचाने के थोड़ा अजीब तरह से बचा जाता है लगता है
stevec

क्या यह धागा सुरक्षित है, उदाहरण के लिए जब समानांतर में परीक्षण चल रहा हो?
एंटन टोलेन

@AntonTolken मैंने इसे आज़माया नहीं है, लेकिन मेरे उदाहरण में, अद्यतन की गई वस्तु स्थानीय चर (saveObject) है, इसलिए इसे थ्रेड सुरक्षित होना चाहिए।
रिच टेब जूल

113

मैं एक ही तरीके से कॉल सत्यापित कर रहा हूं - मेरा मानना ​​है कि यह इसे करने का सही तरीका है।

mockSomething.Verify(ms => ms.Method(
    It.IsAny<int>(), 
    It.Is<MyObject>(mo => mo.Id == 5 && mo.description == "test")
  ), Times.Once());

यदि आपकी लैम्ब्डा अभिव्यक्ति अस्पष्ट हो जाती है, तो आप एक फ़ंक्शन बना सकते हैं जो MyObjectइनपुट और आउटपुट true/ के रूप में लेता है false...

mockSomething.Verify(ms => ms.Method(
    It.IsAny<int>(), 
    It.Is<MyObject>(mo => MyObjectFunc(mo))
  ), Times.Once());

private bool MyObjectFunc(MyObject myObject)
{
  return myObject.Id == 5 && myObject.description == "test";
}

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

EDIT: यहां उन परिदृश्यों के लिए कई बार कॉल को सत्यापित करने का एक उदाहरण है जहां आप यह सत्यापित करना चाहते हैं कि आप किसी सूची में प्रत्येक ऑब्जेक्ट के लिए फ़ंक्शन कहते हैं (उदाहरण के लिए)।

foreach (var item in myList)
  mockRepository.Verify(mr => mr.Update(
    It.Is<MyObject>(i => i.Id == item.Id && i.LastUpdated == item.LastUpdated),
    Times.Once());

सेटअप के लिए एक ही दृष्टिकोण ...

foreach (var item in myList) {
  var stuff = ... // some result specific to the item
  this.mockRepository
    .Setup(mr => mr.GetStuff(item.itemId))
    .Returns(stuff);
}

इसलिए हर बार GetStuff को उस आइटम के लिए बुलाया जाता है, यह उस आइटम के लिए विशिष्ट सामान लौटाएगा। वैकल्पिक रूप से, आप एक फ़ंक्शन का उपयोग कर सकते हैं जो आइटमआईड को इनपुट के रूप में लेता है और सामान लौटाता है।

this.mockRepository
    .Setup(mr => mr.GetStuff(It.IsAny<int>()))
    .Returns((int id) => SomeFunctionThatReturnsStuff(id));

एक अन्य विधि जो मैंने कुछ समय पहले एक ब्लॉग पर देखी थी (फिल हैक शायद?) में किसी प्रकार के डीक्यू ऑब्जेक्ट से लौटने पर सेटअप था - हर बार जब यह कहा जाता था कि यह एक कतार से एक आइटम खींचता है।


1
धन्यवाद, यह मेरे लिए समझ में आता है। मुझे अभी भी समझ नहीं आ रहा है कि सेटअप या सत्यापन में विवरण निर्दिष्ट कब किया जाए। यह काफी भ्रामक है। फिलहाल, मैं सेटअप में कुछ भी अनुमति दे रहा हूं और सत्यापन में मूल्यों को निर्दिष्ट कर रहा हूं।
लुइस मिराबल

आपको कैसे लगता है कि मैं कई कॉल करने पर संदेशों की जांच कर सकता हूं? क्लाइंट संदेश ले जाता है और कई अलग-अलग संदेश भेज सकता है, जो कई कॉल में और उन सभी कॉल में समाप्त हो जाएगा, मुझे विभिन्न संदेशों की जांच करनी होगी। मैं अभी भी सामान्य रूप से इकाई परीक्षण के साथ संघर्ष कर रहा हूं, मैं अभी तक इससे परिचित नहीं हूं।
लुइस मिराबल

मुझे नहीं लगता कि आपको यह कैसे करना चाहिए के संदर्भ में एक जादू चांदी की गोली है। अभ्यास होता है और आप बेहतर होने लगते हैं। मेरे लिए, मैं केवल उन मापदंडों को निर्दिष्ट करता हूं जब मेरे पास उनकी तुलना करने के लिए कुछ होता है और जब मैं पहले से ही किसी अन्य परीक्षण में उस पैरामीटर का परीक्षण नहीं कर रहा होता हूं। कई कॉल के लिए कई दृष्टिकोण हैं। एक फ़ंक्शन को सेट करने और सत्यापित करने के लिए, जिसे कई बार कहा जाता है, मैं आमतौर पर प्रत्येक कॉल के लिए सेटअप या सत्यापन (Times.Once ()) कहता हूं, जो मैं उम्मीद करता हूं - अक्सर लूप के लिए। आप प्रत्येक कॉल को अलग करने के लिए विशिष्ट मापदंडों का उपयोग कर सकते हैं।
मेयो

मैंने कई कॉल के लिए कुछ उदाहरण जोड़े - ऊपर उत्तर देखें।
मेयो

1
"इसके अलावा, मॉक के साथ एक बग से अवगत रहें जहां त्रुटि संदेश बताता है कि विधि को कई बार कॉल किया गया था जब इसे बिल्कुल भी नहीं बुलाया गया था। उन्होंने अब तक इसे ठीक कर लिया होगा - लेकिन अगर आप उस संदेश को देखते हैं तो आप इसे सत्यापित करने पर विचार कर सकते हैं। विधि वास्तव में बुलाया गया था। " - इस तरह एक बग पूरी तरह से एक नकली लाइब्रेरी IMHO को अमान्य कर देता है। कैसे विडंबना है कि वे इसके लिए उचित परीक्षण कोड नहीं है :-)
Gianluca Ghettini

20

एक सरल तरीका होगा:

ObjectA.Verify(
    a => a.Execute(
        It.Is<Params>(p => p.Id == 7)
    )
);

मुझे यह काम नहीं मिल रहा है, मैं यह सत्यापित करने की कोशिश कर रहा हूं कि मेरे तरीके को एक पैरामीटर के रूप में Code.WRCC के साथ बुलाया गया था। लेकिन मेरा परीक्षण हमेशा गुजरता है, भले ही पैरामीटर पारित हो गया है WRDD .. m.Setup(x => x.CreateSR(Code.WRDD)).ReturnsAsync(0); await ClassUnderTest.CompleteCC(accountKey, It.IsAny<int>(), mockRequest); m.Verify(x => x.CreateSR(It.Is<Code>(p => p == Code.WRCC)), Times.Once());
ग्रेग क्विन

1

मेरा मानना ​​है कि इस तथ्य में समस्या है कि Moq समानता के लिए जाँच करेगा। और, चूंकि XmlElement इक्वाल्स को ओवरराइड नहीं करता है, इसलिए यह कार्यान्वयन संदर्भ समानता के लिए जाँच करेगा।

क्या आप एक कस्टम ऑब्जेक्ट का उपयोग नहीं कर सकते, ताकि आप बराबरी से ओवरराइड कर सकें?


हां, मैंने ऐसा करना समाप्त कर दिया। मुझे एहसास हुआ कि समस्या Xml की जाँच कर रही थी। प्रश्न के दूसरे भाग में मैंने xml को एक वस्तु से जोड़ते हुए एक संभावित उत्तर जोड़ा
लुइस मिराबल

1

इनमें से एक भी था, लेकिन कार्रवाई का पैरामीटर एक सार्वजनिक गुणों वाला इंटरफ़ेस था। एक अलग विधि के साथ It.Is () का उपयोग करके समाप्त हुआ और इस पद्धति के भीतर इंटरफ़ेस का कुछ मज़ाक करना पड़ा

public interface IQuery
{
    IQuery SetSomeFields(string info);
}

void DoSomeQuerying(Action<IQuery> queryThing);

mockedObject.Setup(m => m.DoSomeQuerying(It.Is<Action<IQuery>>(q => MyCheckingMethod(q)));

private bool MyCheckingMethod(Action<IQuery> queryAction)
{
    var mockQuery = new Mock<IQuery>();
    mockQuery.Setup(m => m.SetSomeFields(It.Is<string>(s => s.MeetsSomeCondition())
    queryAction.Invoke(mockQuery.Object);
    mockQuery.Verify(m => m.SetSomeFields(It.Is<string>(s => s.MeetsSomeCondition(), Times.Once)
    return true
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.