在下面的代码示例中,我有一个Async Calculator类。这是注入一个ICalc,它将是一个同步计算器。我使用依赖注入和模拟ICalc,因为这类似于我的真实场景,尽管我认为模拟与问题无关。 AsyncCalc有一个函数,它将异步调用另一个函数 - 将回调作为参数。当异步函数调用完成时,将使用结果触发回调。
现在我想测试我的异步函数 - 检查是否使用期望参数触发了回调。这段代码似乎有效。但是,我觉得它可能随时都会爆炸 - 我担心的是在函数结束和测试终止之前完成回调的竞争条件 - 因为这将在一个单独的线程中运行。
我现在的问题是,如果我在正确的轨道单元上测试异步功能,或者是否有人可以帮助我走上正轨......?更好的是,如果我能确保立即触发回调 - 最好是在同一个线程上我猜?可以/应该这样做吗?
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);
}
答案 0 :(得分:23)
您可以使用ManualResetEvent来同步您的主题。
在以下示例中,测试线程将阻止对completion.WaitOne()
的调用。
异步计算的回调存储结果,然后通过调用completion.Set()
来表示事件。
[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);
// }
答案 1 :(得分:0)
您还可以使用“test runner”类在循环中运行断言。循环将在try / catch中运行断言。异常处理程序只会尝试再次运行断言,直到超时到期。我最近写了一篇关于这种技术的博客文章。这个例子是groovy,但适用于任何语言。您可以在c#。
中传递一个Action,而不是传递Closurehttp://www.greenmoonsoftware.com/2013/08/asynchronous-functional-testing/