2010-01-21 11 views
19

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

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

मेरा प्रश्न अब यह है कि अगर मैं सही ट्रैक इकाई पर एसिंक फ़ंक्शन का परीक्षण कर रहा हूं, या कोई मुझे सही रास्ते पर जाने में मदद कर सकता है ..? बेहतर क्या होगा अगर मैं यह सुनिश्चित कर सकता हूं कि कॉलबैक तुरंत ट्रिगर हो गया है - और अधिमानतः उसी धागे पर मुझे लगता है? क्या किया जा सकता है?

public interface ICalc 
{ 
    int AddNumbers(int a, int b); 
} 

public class AsyncCalc 
{ 
    private readonly ICalc _calc; 
    public delegate void ResultProcessor(int result); 
    public delegate int AddNumbersAsyncCaller(int a, int b); 

    public AsyncCalc(ICalc calc) 
    { 
     _calc = calc; 
    } 

    public void AddNumbers(int a, int b, ResultProcessor resultProcessor) 
    { 
     var caller = new AddNumbersAsyncCaller(_calc.AddNumbers); 
     caller.BeginInvoke(a, b, new AsyncCallback(AddNumbersCallbackMethod), resultProcessor); 
    } 

    public void AddNumbersCallbackMethod(IAsyncResult ar) 
    { 
     var result = (AsyncResult)ar; 
     var caller = (AddNumbersAsyncCaller)result.AsyncDelegate; 
     var resultFromAdd = caller.EndInvoke(ar); 

     var resultProcessor = ar.AsyncState as ResultProcessor; 
     if (resultProcessor == null) return; 

     resultProcessor(resultFromAdd); 
    }    
} 

[Test] 
public void TestingAsyncCalc() 
{ 
    var mocks = new MockRepository(); 
    var fakeCalc = mocks.DynamicMock<ICalc>(); 

    using (mocks.Record()) 
    { 
     fakeCalc.AddNumbers(1, 2); 
     LastCall.Return(3); 
    } 

    var asyncCalc = new AsyncCalc(fakeCalc); 
    asyncCalc.AddNumbers(1, 2, TestResultProcessor); 
} 

public void TestResultProcessor(int result) 
{ 
    Assert.AreEqual(3, result); 
} 
+1

मैं अपने परीक्षण बस एक पाश में प्रतीक्षा करने के लिए जब तक कॉलबैक कार्यान्वित करना होगा लगता होगा। –

उत्तर

20

आप एक ManualResetEvent का उपयोग अपने धागे सिंक्रनाइज़ करने के लिए कर सकता है।

निम्नलिखित उदाहरण में, परीक्षण धागा कॉल पर completion.WaitOne() पर अवरुद्ध होगा। एसिंक गणना के लिए कॉलबैक परिणाम संग्रहीत करता है और तबcompletion.Set() पर कॉल करके ईवेंट को सिग्नल करता है।

[Test] 
public void TestingAsyncCalc() 
{ 
    var mocks = new MockRepository(); 
    var fakeCalc = mocks.DynamicMock<ICalc>(); 

    using (mocks.Record()) 
    { 
     fakeCalc.AddNumbers(1, 2); 
     LastCall.Return(3); 
    } 

    var asyncCalc = new AsyncCalc(fakeCalc); 

    var completion = new ManualResetEvent(false); 
    int result = 0; 
    asyncCalc.AddNumbers(1, 2, r => { result = r; completion.Set(); }); 
    completion.WaitOne(); 

    Assert.AreEqual(3, calcResult); 
} 

// ** USING AN ANONYMOUS METHOD INSTEAD 
// public void TestResultProcessor(int result) 
// { 
//  Assert.AreEqual(3, result); 
// } 
+0

Freakin भयानक! अच्छी तरह से काम। मुझे जिस चीज की जरूरत थी। धन्यवाद! – stiank81

+3

मैं केवल कुछ परिदृश्यों (बाहरी सेवा) के लिए जोड़ता हूं, आप एक टाइमआउट जोड़ना चाहते हैं: Assert.IsTrue (complet.WaitOne (TimeSpan.FromSeconds (10)), "गणना पूर्ण"); – Rashack

0

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

http://www.greenmoonsoftware.com/2013/08/asynchronous-functional-testing/

संबंधित मुद्दे