2013-03-24 15 views
9

जब मैं विजुअल स्टूडियो 2012 में कोड कवरेज का विश्लेषण करता हूं, तो एसिंक विधियों में से किसी भी प्रतीक्षा लाइन को कवर नहीं किया जा रहा है, भले ही वे मेरे परीक्षण पास होने के बाद स्पष्ट रूप से निष्पादित हो रहे हों। कोड कवरेज रिपोर्ट का कहना है कि अनदेखा विधि MoveNext है, जो मेरे कोड में मौजूद नहीं है (शायद यह संकलक-जेनरेट है)।एसिंक विधियों के लिए कोड कवरेज

क्या एसिंक विधियों के लिए कोड कवरेज रिपोर्टिंग को ठीक करने का कोई तरीका है?

नोट:

मैं सिर्फ Ncover का उपयोग कर कवरेज भाग गया, और कवरेज संख्या बहुत अधिक है कि उपकरण का उपयोग कर कोई मतलब। अभी के लिए एक कामकाज के रूप में, मैं उस पर स्विच कर रहा हूँ।

उत्तर

4

यह आमतौर पर तब हो सकता है जब आप जिस ऑपरेशन का इंतजार कर रहे हैं वह पूरा होने से पहले पूरा हो गया हो।

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

+1

तरीकों सब पूरा कर रहे हैं, और परीक्षण से गुजर रहे हैं। ऐसा लगता है कि मुझे उपकरण की सीमा का सामना करना पड़ रहा है। – Jacob

+0

ठीक है, लेकिन 'इंतजार' के बिंदु पर ऑपरेशन पहले से ही पूरा हो चुके हैं? –

+0

गोटा ... तो आपको इंतजार करने के प्रत्येक उदाहरण के लिए वास्तव में उन परिदृश्यों का परीक्षण करना होगा? यदि आपके पास 5 इंतजार के साथ कोई तरीका है, तो आपको 100% कवरेज प्राप्त करने के लिए कम से कम 15 परीक्षण केस लिखना होगा? यह मेरे लिए एक बग की तरह लगता है। ऐसा लगता है कि आपके कोड का परीक्षण करने से कंपाइलर द्वारा उत्सर्जित एसिंक तंत्र का परीक्षण करना मेरे लिए अधिक लगता है। – Jacob

-1

मैंने एक परीक्षण धावक बनाया जो कई बार कोड के ब्लॉक को चलाता है और उस फैक्ट्री का उपयोग करके देरी से भिन्न होता है। कोड के सरल ब्लॉक के माध्यम से विभिन्न पथों का परीक्षण करने के लिए यह बहुत अच्छा है। अधिक जटिल पथों के लिए आप प्रति पथ एक परीक्षण बनाना चाहते हैं।

[TestMethod] 
public async Task ShouldTestAsync() 
{ 
    await AsyncTestRunner.RunTest(async taskFactory => 
    { 
     this.apiRestClient.GetAsync<List<Item1>>(NullString).ReturnsForAnyArgs(taskFactory.Result(new List<Item1>())); 
     this.apiRestClient.GetAsync<List<Item2>>(NullString).ReturnsForAnyArgs(taskFactory.Result(new List<Item2>())); 

     var items = await this.apiController.GetAsync(); 

     this.apiRestClient.Received().GetAsync<List<Item1>>(Url1).IgnoreAwait(); 
     this.apiRestClient.Received().GetAsync<List<Item2>>(Url2).IgnoreAwait(); 

     Assert.AreEqual(0, items.Count(), "Zero items should be returned."); 
    }); 
} 

public static class AsyncTestRunner 
{ 
    public static async Task RunTest(Func<ITestTaskFactory, Task> test) 
    { 
     var testTaskFactory = new TestTaskFactory(); 
     while (testTaskFactory.NextTestRun()) 
     { 
      await test(testTaskFactory); 
     } 
    } 
} 

public class TestTaskFactory : ITestTaskFactory 
{ 
    public TestTaskFactory() 
    { 
     this.firstRun = true; 
     this.totalTasks = 0; 
     this.currentTestRun = -1; // Start at -1 so it will go to 0 for first run. 
     this.currentTaskNumber = 0; 
    } 

    public bool NextTestRun() 
    { 
     // Use final task number as total tasks. 
     this.totalTasks = this.currentTaskNumber; 

     // Always return has next as turn for for first run, and when we have not yet delayed all tasks. 
     // We need one more test run that tasks for if they all run sync. 
     var hasNext = this.firstRun || this.currentTestRun <= this.totalTasks; 

     // Go to next run so we know what task should be delayed, 
     // and then reset the current task number so we start over. 
     this.currentTestRun++; 
     this.currentTaskNumber = 0; 
     this.firstRun = false; 

     return hasNext; 
    } 

    public async Task<T> Result<T>(T value, int delayInMilliseconds = DefaultDelay) 
    { 
     if (this.TaskShouldBeDelayed()) 
     { 
      await Task.Delay(delayInMilliseconds); 
     } 

     return value; 
    } 

    private bool TaskShouldBeDelayed() 
    { 
     var result = this.currentTaskNumber == this.currentTestRun - 1; 
     this.currentTaskNumber++; 
     return result; 
    } 

    public async Task VoidResult(int delayInMilliseconds = DefaultDelay) 
    { 
     // If the task number we are on matches the test run, 
     // make it delayed so we can cycle through them. 
     // Otherwise this task will be complete when it is reached. 
     if (this.TaskShouldBeDelayed()) 
     { 
      await Task.Delay(delayInMilliseconds); 
     } 
    } 

    public async Task<T> FromResult<T>(T value, int delayInMilliseconds = DefaultDelay) 
    { 
     if (this.TaskShouldBeDelayed()) 
     { 
      await Task.Delay(delayInMilliseconds); 
     } 

     return value; 
    } 
} 
2

स्थितियों में, जहां मैं एक विधि के async प्रकृति का परीक्षण के बारे में परवाह नहीं है लेकिन सिर्फ आंशिक कोड कवरेज से छुटकारा पाने के लिए चाहते हैं। मैं इससे बचने के लिए नीचे विस्तार विधि का उपयोग करता हूं और यह मेरे लिए ठीक काम करता है।

चेतावनी "थ्रेड। नींद" यहां उपयोग किया गया!

public static IReturnsResult<TClass> ReturnsAsyncDelayed<TClass, TResponse>(this ISetup<TClass, Task<TResponse>> setup, TResponse value) where TClass : class 
{ 
    var completionSource = new TaskCompletionSource<TResponse>(); 
    Task.Run(() => { Thread.Sleep(200); completionSource.SetResult(value); }); 
    return setup.Returns(completionSource.Task); 
} 

और उपयोग Moq के ReturnsAsync सेटअप के समान है।

_sampleMock.Setup(s => s.SampleMethodAsync()).ReturnsAsyncDelayed(response); 
1

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

यदि आप उस कार्य का उपयोग करते हैं जो कोड को कवर करने के समय पूरा नहीं होता है, तो संकलक-जेनरेट की गई राज्य मशीन कार्य पूरा होने पर फिर से शुरू करने के लिए पूर्ण कॉलबैक को हुक करता है। यह पूरी तरह से राज्य मशीन कोड का उपयोग करता है, और परिणामस्वरूप पूर्ण कोड कवरेज (कम से कम कथन-स्तर कोड कवरेज टूल के लिए)।

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

एक बेहतर विकल्प का उपयोग करने के लिए है "का इंतजार है Task.Yield()"। यह तुरंत वापस आ जाएगा, लेकिन जैसे ही यह सेट हो रहा है, निरंतरता का आह्वान करें।

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

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

एक इस हैक के अधिक पूर्ण विवरण के यहां पाया जा सकता है: http://blogs.msdn.com/b/dwayneneed/archive/2014/11/17/code-coverage-with-async-await.aspx

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