unit testing异步function

在下面的代码示例中,我有一个Async Calculator类。 这是注入一个ICalc,它将是一个同步计算器。 我使用dependency injection和模拟ICalc,因为这类似于我的真实场景,尽管我猜嘲笑并不是真正与问题相关。 AsyncCalc有一个函数,它将异步调用另一个函数 – 将回调作为参数。 当异步函数调用完成时,将使用结果触发回调。

现在我想测试我的异步函数 – 检查回调是否被期望参数触发。 这段代码似乎有效。 但是,我觉得它可能随时都会爆炸 – 我的担心是在函数结束和测试终止之前完成回调的竞争条件 – 因为这将在一个单独的线程中运行。

我现在的问题是,如果我在正确的轨道单元上测试异步function,或者是否有人可以帮助我走上正轨……? 更好的是,如果我能确保立即触发回调 – 最好是在同一个线程上我猜? 可以/应该这样做吗?

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(); 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); } 

您可以使用ManualResetEvent来同步线程。

在以下示例中,测试线程将阻止对completion.WaitOne()的调用。 异步计算的回调存储结果, 然后通过调用completion.Set()指示事件。

 [Test] public void TestingAsyncCalc() { var mocks = new MockRepository(); var fakeCalc = mocks.DynamicMock(); 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); // } 

您还可以使用“test runner”类在循环中运行断言。 循环将在try / catch中运行断言。 exception处理程序只会尝试再次运行断言,直到超时过期。 我最近写了一篇关于这种技术的博客文章。 示例是groovy,但适用于任何语言。 您将在c#中传递一个Action,而不是传递Closure。

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