2010-01-21 23 views
19

Aşağıdaki kod örneğinde Async Calculator sınıfına sahibim. Bu, eşzamanlı bir hesap makinesi olacak bir ICalc ile enjekte edilir. Bağımlılık enjekte etmek ve ICalc ile alay etmek için kullanıyorum çünkü bu benim gerçek senaryonumu andırıyor, ancak alayın gerçekten de konuyla ilgisi olmadığını düşünüyorum. AsyncCalc, başka bir işlevi eşzamansız olarak çağıran bir işleve sahiptir - geri arama parametresi olarak. Zaman uyumsuz işlev çağrısı bittiğinde, geri arama sonucuyla tetiklenir.Asenkron işlevi sınama birimi

Şimdi asenkron işlevimi test etmek istiyorum - geri çağrının beklenen parametre ile tetiklendiğini kontrol etmek. Bu kod işe yarayacak gibi görünüyor. Ancak, herhangi bir zamanda patlayabileceğini hissediyorum - ve endişem, işlev sona ermeden ve test sonlandırılmadan önce bitirmenin geri arama koşuludur - çünkü bu işlem ayrı bir iş parçacığında yürütülecektir.

Sorum şu anda async işlevini test eden doğru birim birimindeyim ya da herkes doğru parçaya geçmeme yardımcı olabilir mi? Daha iyi ne hisseder, geri çekilmenin hemen tetiklenmesini sağlayabilir miyim - ve tercihen aynı iş parçacığı üzerinde sanırım? Yapılabilir mi/Olmalı mı?

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

ben test basitçe geri arama yürütür kadar bir döngü içinde beklemek zorunda kalacak düşünürdüm. –

cevap

20

Konularınızı senkronize etmek için ManualResetEvent kullanabilirsiniz.

Aşağıdaki örnekte, test iş parçacığı aramayı completion.WaitOne() numaralı telefona engelleyecektir. Async hesaplaması için geri arama sonucu kaydeder ve sonra olayları completion.Set() numaralı telefonu arayarak bildirir.

[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 harika! Mükemmel çalışıyor. Tam ihtiyacım olan şey. Teşekkürler! – stiank81

+3

Sadece belirli senaryolar için (harici servis) bir zaman aşımı eklemek isteyebilirsiniz: Assert.IsTrue (completion.WaitOne (TimeSpan.FromSeconds (10)), "Hesaplama tamamlandı"); – Rashack

0

Ayrıca, bir döngü içinde asserts'i çalıştırmak için "test koşucusu" sınıfı da kullanabilirsiniz. Döngü, denemeleri bir try/catch içinde çalıştırır. İstisna işleyici, zaman aşımı süresi doluncaya kadar yalnızca tekrarları çalıştırmayı dener. Geçenlerde bu teknikle ilgili bir blog yazısı yazdım. Örnek groovy, ancak herhangi bir dile uygulanabilir. Bir Kapanış geçmek yerine, C# 'da bir Eylem geçirirsiniz.

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

İlgili konular