Tes unit untuk panggilan delegasi asinkron di c#

Saya memiliki fungsi yang membuat delegasi dan memulai BeginInvoke pada objek itu, dengan fungsi lain diteruskan untuk menunggu EndInvoke:

    private static void DeploymentComponentThreadedCallBack(IAsyncResult ar)
    {
        var result = (AsyncResult)ar;
        var pluginExecuteAction = (Action<int, Guid, int, EnvironmentServerComponentSet, string>)result.AsyncDelegate;
        pluginExecuteAction.EndInvoke(ar);
        //report back to WCF service that thread is finished
    }

public void DeployComponent(byte[] resource, Guid componentGuid, string deploymentType, Dictionary<string, object> args)
{
  var asyncCallback = new AsyncCallback(DeploymentComponentThreadedCallBack);
  IDeployComponent plugin = GetPluginDelegate();
  Action<byte[], Guid, string, Dictionary<string, object>> pluginExecuteAction = plugin.DeployComponent;
  IAsyncResult ar = pluginExecuteAction.BeginInvoke(resource, componentGuid, deploymentType, args, asyncCallback, null);
}

Saya ingin menguji unit ini, tetapi ketika saya melakukannya, DeploymentComponentThreadedCallBack tidak pernah terkena, dan tentu saja panggilan EndInvoke juga tidak. Saya berasumsi ini terjadi karena pengujian berlalu sebelum thread asynchronous berakhir, sehingga thread berhenti dieksekusi sebelum EndInvoke, tetapi apakah ada cara agar saya dapat menghentikan hal ini sehingga saya dapat melihat bahwa EndInvoke terkena?

Salam, Mat


person Bob Tway    schedule 31.08.2011    source sumber


Jawaban (3)


Saya rasa masalah mendasar Anda adalah Anda tidak mengekspos apa pun pada metode DeployComponent yang memungkinkan Anda melacak operasi asinkron yang Anda mulai dari sana. Jika Anda mengembalikan IAsyncResult dari sana, Anda dapat menghubungi ar.AsyncWaitHandle.WaitOne() untuk menunggu hingga selesai.

person RandomEngy    schedule 31.08.2011
comment
Sayangnya ini adalah kode contoh - pada kenyataannya DeployComponent melakukan lebih banyak hal selain itu dan sudah memiliki tipe pengembalian bool. Terima kasih atas sarannya. - person Bob Tway; 01.09.2011
comment
Ya, Anda harus menyelesaikan masalah itu. Jika Anda ingin menunggu hingga operasi selesai, Anda harus memaparkan sesuatu agar penelepon dapat menunggu. Dalam keadaan normal Anda melanjutkan dan tidak menunggu sampai selesai, namun dalam pengujian Anda, Anda menunggu sampai selesai. Anda sebenarnya bisa mengubah DeployComponent untuk mengikuti APM dan melaporkan penyelesaian ketika semua hal yang dimulai selesai. Mungkin perlu sedikit pemfaktoran ulang. - person RandomEngy; 02.09.2011

Sejauh yang saya ingat AsyncResult memiliki tanda (IsCompleted) yang memberitahukan Anda apakah operasi sedang berlangsung. Tunggu (misalnya secara primitif dengan perulangan while), lalu lakukan pernyataan Anda

person flq    schedule 31.08.2011
comment
Ada tanda seperti itu, tapi bagaimana cara mendapatkannya di objek panggilan? yaitu Jika metode DeployComponent ada pada objek Foo, dan dalam pengujian saya, saya memanggil Foo.DeployComponent(byteStream, new Guid(), cmd, null); di mana saya bisa mendapatkan AsyncResult untuk melihat apakah sudah selesai? - person Bob Tway; 31.08.2011
comment
Saya tidak bisa melihat dari mana Anda datang. Anda meneruskan Asyncresult ke metode pertama, jadi Anda bisa menunggu sebelum meneruskannya. - person flq; 31.08.2011
comment
Ah, aku mengerti maksudmu. Itu tidak akan berhasil, karena dalam keadaan normal saya tidak ingin menunggu - hanya untuk tes ini. Kalau tidak, tidak ada gunanya melakukannya secara asinkron. - person Bob Tway; 31.08.2011
comment
Matt, yang hilang adalah ujianmu - tidak terlihat dari mana asalmu. Jika Anda tidak memberikan akses ke AsyncHandle, Anda mungkin kurang beruntung - person flq; 31.08.2011

Anda hanya perlu membuat titik injeksi untuk mengubah panggilan asinkron menjadi panggilan pemblokiran. Misalnya:

 public class MethodInvoker
 {
     public virtual void Invoke(Action begin, Action<IAsyncResult> end)
     {
          begin.BeginInvoke(end, null);
     }
 }

Dengan versi pengujian unit seperti:

 public class SynchronousInvoker : MethodInvoker
 {
     public override void Invoke(Action begin, Action<IAsyncResult> end)
     {
         begin();
         end();
     }
 }

Kemudian Anda akan menulis kode seperti ini:

 _myMethodInvoker.Invoke(pluginExecuteAction, asyncCallback);

Yang dalam konteks fungsi normal Anda, tidak sinkron. Dalam pengujian unit Anda, Anda cukup memasukkan SynchronousInvoker di tempatnya dan itu menjadi panggilan pemblokiran.

person Tejs    schedule 31.08.2011
comment
Ini sepertinya pendekatan yang bagus, tapi saya tidak bisa mengubahnya agar berhasil. BeginInvoke memerlukan parameter bertipe AsyncResult, bukan Action‹IAsyncResult› dan tanpa Action Anda tidak dapat memanggil end() di dalamnya. - person Bob Tway; 01.09.2011