Testing asynchronous methods in .NET (or, how to make an async method synchronous)


/ Published in: C#
Save to your folder(s)

Since an asynchronous method is usually one you call and then it invokes a callback when it is finished, it is not obvious how to test such a method in a definite manner. In the method described at the above URL, the author uses .NET's System.Threading.AutoResetEvent, along with a member variable, to notify the primary thread that the test method has finished. The source below (from Radiant::Mirage::Empire) tests a method called ReadVersion from an API which fires events at the completion of each command or a special event if there is an error. The static constructor initializes the API, subscribes to the Error event, and adds Trace output to the Console. The two event listeners simply log data (to Trace or to a member variable) and then set the AutoResetEvent so that the calling method can proceed. AsyncTest is a method that allows me to call a method and then perform the same, standard procedures afterwards (creating the AutoResetEvent, waiting for the thread to return, and outputting to Trace). The actual test method, ReadVersionTest, calls AsyncTest with a lambda (which is an anonymous method that gets called inside AsyncTest) that subscribes to the relevant event and issues the specified command. The ReadVersionTestComplete method shows what the code would look like without using the AsyncTest method and lambda.


Copy this code and paste it in your HTML
  1. using Empire;
  2. using Microsoft.VisualStudio.TestTools.UnitTesting;
  3. using System;
  4. using System.Threading;
  5. using System.Diagnostics;
  6. using System.Collections.Generic;
  7. using System.Text;
  8.  
  9. namespace snippets
  10. {
  11. [TestClass()]
  12. public class EmpireApiTest
  13. {
  14. private static AutoResetEvent m_testTrigger;
  15. private object m_testData;
  16. private int m_timeout = 1000 * 60 * 1; // 1 minute
  17.  
  18. static EmpireApiTest()
  19. {
  20. Trace.Listeners.Add(new ConsoleTraceListener(false)); // For ReSharper unit test output
  21. EmpireApi.Initialize(IP, PORT);
  22. EmpireApi.ErrorRcvd += EmpireApi_ErrorRcvd;
  23. }
  24.  
  25. static void EmpireApi_ErrorRcvd(object sender, EventArgs<string> e)
  26. {
  27. Trace.TraceError(e.ToString());
  28. m_testTrigger.Set();
  29. //Assert.Fail(e.ToString());
  30. }
  31.  
  32. void Receive(object sender, EventArgs e)
  33. {
  34. m_testData = e;
  35. m_testTrigger.Set();
  36. }
  37.  
  38. public void AsyncTest(Action method, string test)
  39. {
  40. method();
  41. m_testTrigger = new AutoResetEvent(false);
  42. bool didReturn = m_testTrigger.WaitOne(m_timeout);
  43. if (!didReturn)
  44. {
  45. Assert.Fail(test);
  46. }
  47. Trace.WriteLine(String.Format("{0}:
  48. {1}",
  49. test,
  50. m_testData));
  51. }
  52.  
  53. [TestMethod()]
  54. public void ReadVersionTest()
  55. {
  56. AsyncTest(
  57. () =>
  58. {
  59. EmpireApi.ReadVersion_Rcvd += Receive;
  60. EmpireApi.ReadVersion();
  61. },
  62. "ReadVersionTest"
  63. );
  64. }
  65.  
  66. [TestMethod()]
  67. public void ReadVersionTestComplete()
  68. {
  69. EmpireApi.ReadVersion_Rcvd += Receive;
  70. EmpireApi.ReadVersion();
  71. m_testTrigger = new AutoResetEvent(false);
  72. bool didReturn = m_testTrigger.WaitOne(m_timeout);
  73. if (!didReturn)
  74. {
  75. Assert.Fail("ReadVersionTest");
  76. }
  77. Trace.WriteLine(String.Format("{0}:
  78. {1}",
  79. "ReadVersionTest",
  80. m_testData));
  81. }
  82. }
  83. }

URL: http://jasondotnet.spaces.live.com/blog/cns!BD40DBF53845E64F!170.entry

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.