एक यूनिट परीक्षण के लिए एक अतुल्यकालिक विधि का उपयोग करने के लिए Moq का उपयोग करना


180

मैं एक सेवा के लिए एक विधि का परीक्षण कर रहा हूं जो वेब APIकॉल करती है। HttpClientयदि मैं वेब सेवा (समाधान में किसी अन्य परियोजना में स्थित) को स्थानीय रूप से चलाता हूं, तो यूनिट परीक्षणों के लिए सामान्य कार्यों का उपयोग करना ठीक है।

हालाँकि जब मैं अपने परिवर्तनों की जाँच करता हूँ तो बिल्ड सर्वर की वेब सेवा तक पहुँच नहीं होगी इसलिए परीक्षण विफल हो जाएंगे।

मैंने अपने यूनिट परीक्षणों के लिए एक IHttpClientइंटरफ़ेस बनाकर और एक संस्करण को लागू करने के लिए इसके चारों ओर एक रास्ता तैयार किया है, जिसे मैं अपने आवेदन में उपयोग करता हूं। यूनिट परीक्षणों के लिए, मैं एक नकली असिंक्रोनस पोस्ट विधि के साथ एक नकली संस्करण को पूरा करता हूं। यहां मैं समस्याओं में चला गया हूं। मैं HttpStatusResultइस विशेष परीक्षा के लिए ओके लौटना चाहता हूं । एक और समान परीक्षण के लिए मैं एक बुरा परिणाम लौटाऊंगा।

परीक्षा चलेगी लेकिन कभी पूरी नहीं होगी। यह प्रतीक्षा में लटका रहता है। मैं अतुल्यकालिक प्रोग्रामिंग, डेलीगेट्स और खुद Moq में नया हूँ और नई चीजों को सीखने के लिए कुछ समय के लिए SO और google को खोज रहा हूं, लेकिन मैं अभी भी इस समस्या को हल नहीं कर सकता।

यहाँ वह विधि है जिसे मैं परखने की कोशिश कर रहा हूँ:

public async Task<bool> QueueNotificationAsync(IHttpClient client, Email email)
{
    // do stuff
    try
    {
        // The test hangs here, never returning
        HttpResponseMessage response = await client.PostAsync(uri, content);

        // more logic here
    }
    // more stuff
}

यहाँ मेरी इकाई परीक्षण विधि है:

[TestMethod]
public async Task QueueNotificationAsync_Completes_With_ValidEmail()
{
    Email email = new Email()
    {
        FromAddress = "bob@example.com",
        ToAddress = "bill@example.com",
        CCAddress = "brian@example.com",
        BCCAddress = "ben@example.com",
        Subject = "Hello",
        Body = "Hello World."
    };
    var mockClient = new Mock<IHttpClient>();
    mockClient.Setup(c => c.PostAsync(
        It.IsAny<Uri>(),
        It.IsAny<HttpContent>()
        )).Returns(() => new Task<HttpResponseMessage>(() => new HttpResponseMessage(System.Net.HttpStatusCode.OK)));

    bool result = await _notificationRequestService.QueueNotificationAsync(mockClient.Object, email);

    Assert.IsTrue(result, "Queue failed.");
}

मैं क्या गलत कर रहा हूं?

आपके सहयोग के लिए धन्यवाद।

जवाबों:


351

आप एक कार्य बना रहे हैं, लेकिन इसे कभी भी शुरू न करें, इसलिए यह कभी पूरा नहीं होता है। हालाँकि, केवल कार्य शुरू न करें - इसके बजाय, उपयोग करने के लिए बदलें Task.FromResult<TResult>जिससे आपको एक कार्य मिलेगा जो पहले ही पूरा हो चुका है:

...
.Returns(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)));

ध्यान दें कि आप इस तरह से वास्तविक अतुल्यकालिक परीक्षण नहीं करेंगे - यदि आप ऐसा करना चाहते हैं, तो आपको इसे बनाने के लिए थोड़ा और काम करने की आवश्यकता है, Task<T>जिसे आप अधिक बारीक तरीके से नियंत्रित कर सकते हैं ... लेकिन यह कुछ के लिए है किसी और दिन।

आप IHttpClientहर चीज का मजाक उड़ाने के बजाय नकली का उपयोग करने पर विचार कर सकते हैं - यह वास्तव में इस बात पर निर्भर करता है कि आपको कितनी बार इसकी आवश्यकता है।


2
आपका बहुत बहुत धन्यवाद। वह महान काम किया। मुझे लगा कि यह शायद कुछ सरल है जो मुझे समझ नहीं आ रहा था।
मावेला १५

2
पुन: नकली IHttpClient, मैंने माना कि लेकिन मुझे वेब एपीआई से वापस आने वाले अपेक्षित व्यवहार के आधार पर अलग-अलग परीक्षणों के लिए अलग-अलग HttpStatusCodes को वापस करने में सक्षम होने की आवश्यकता थी, और यह मुझे और अधिक नियंत्रण देने के लिए लग रहा था।
मवेनेला

3
@mvanella: हाँ, इसलिए आप एक नकली बनाएँगे जो आप इसे वापस चाहते हैं। बस कुछ सोचने के लिये।
जॉन स्कीट

134
अब जो कोई भी इसे पाता है, उसके लिए Moq 4.2 में एक एक्सटेंशन है ReturnsAysnc, जो वास्तव में ऐसा करता है।
स्टुअर्ट ग्रासी

3
@legacybass मुझे इसके लिए किसी भी दस्तावेज का लिंक नहीं मिल रहा है, भले ही एपीआई डॉक्स का कहना है कि वे v4.2.1312.1622 के खिलाफ बनाया गया है जो लगभग एक साल पहले जारी किया गया था। देखिए यह कमिट जो उस रिलीज से कुछ दिन पहले बनाई गई थी। एपीआई दस्तावेजों को अपडेट क्यों नहीं किया जाता है ...
स्टुअर्ट ग्रासी

17

ऊपर दिए गए @Stuart Grassie के उत्तर की अनुशंसा करें।

var moqCredentialMananger = new Mock<ICredentialManager>();
moqCredentialMananger
                    .Setup(x => x.GetCredentialsAsync(It.IsAny<string>()))
                    .ReturnsAsync(new Credentials() { .. .. .. });

1

साथ Mock.Of<...>(...)के लिए asyncविधि का उपयोग कर सकते Task.FromResult(...):

var client = Mock.Of<IHttpClient>(c => 
    c.PostAsync(It.IsAny<Uri>(), It.IsAny<HttpContent>()) == Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))
);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.