问题描述
在以下code样品我有一个异步计算器类。此注射用ICalc,这将是一个syncronous计算器。我使用依赖注入,嘲笑ICalc因为这就像我的真实场景,虽然我猜嘲弄不是真正意义的的问题。该AsyncCalc有一个功能,将异步调用另一个函数 - 以回调作为参数。而当异步函数调用结束回调将结果触发。
现在我想测试我的异步函数 - 检查回调触发与预期参数。这code似乎工作。不过,我觉得它随时可能爆炸 - 和我关心的是回调完成操作结束,测试结束前的竞争条件 - 因为这将在一个单独的线程中运行。
我现在的问题是,如果我在正确的轨道单元测试异步功能上,或者,如果有人能帮助我在正确的轨道上..?你会感觉更好,如果我能确保回调触发的时候了 - 和preferably在同一个线程我猜? CAN /它应该做些什么呢?
公共接口ICalc
{
诠释的addNumbers(INT A,INT B);
}公共类AsyncCalc
{
私人只读ICalc _calc;
公共委托无效ResultProcessor(INT结果);
公共委托INT AddNumbersAsyncCaller(INT A,INT B); 公共AsyncCalc(ICalc计算)
{
_calc =计算;
} 公共无效的addNumbers(INT A,INT B,ResultProcessor resultProcessor)
{
VAR呼叫者=新AddNumbersAsyncCaller(_calc.AddNumbers);
caller.BeginInvoke(A,B,新的AsyncCallback(AddNumbersCallbackMethod),resultProcessor);
} 公共无效AddNumbersCallbackMethod(IAsyncResult的AR)
{
VAR的结果=(AsyncResult)AR;
VAR呼叫者=(AddNumbersAsyncCaller)result.AsyncDelegate;
VAR resultFromAdd = caller.EndInvoke(AR); VAR resultProcessor = ar.AsyncState为ResultProcessor;
如果(resultProcessor == NULL)回报; resultProcessor(resultFromAdd);
}
}[测试]
公共无效TestingAsyncCalc()
{
VAR嘲笑=新MockRepository();
VAR fakeCalc = mocks.DynamicMock< ICalc>(); 使用(mocks.Record())
{
fakeCalc.AddNumbers(1,2);
LastCall.Return(3);
} VAR asyncCalc =新AsyncCalc(fakeCalc);
asyncCalc.AddNumbers(1,2,TestResultProcessor);
}公共无效TestResultProcessor(INT结果)
{
Assert.AreEqual(3结果);
}
您可以使用ManualResetEvent同步你的线程。
在下面的例子中,测试线程将阻塞在调用 completion.WaitOne()
。
对于异步计算的回调将结果和的然后的调用信号事件 completion.Set()
。
[测试]
公共无效TestingAsyncCalc()
{
VAR嘲笑=新MockRepository();
VAR fakeCalc = mocks.DynamicMock< ICalc>(); 使用(mocks.Record())
{
fakeCalc.AddNumbers(1,2);
LastCall.Return(3);
} VAR asyncCalc =新AsyncCalc(fakeCalc); VAR完成=新的ManualResetEvent(假);
INT结果为0;
asyncCalc.AddNumbers(1,2,R => {结果= R; completion.Set();});
completion.WaitOne(); Assert.AreEqual(3,calcResult);
}// **使用匿名方法来代替
//公共无效TestResultProcessor(INT结果)
// {
// Assert.AreEqual(3结果);
//}
In the following code sample I have an Async Calculator class. This is injected with an ICalc, which will be a syncronous calculator. I use dependency injecting and mock the ICalc because this resembles my true scenario, though I guess the mocking isn't really of relevance to the question. The AsyncCalc has a function which will call another function asynchronously - taking a callback as parameter. And when the async function call finishes the callback will be triggered with the result.
Now I want to test my asynchronous function - checking that the callback is triggered with the expected parameter. This code seems to work. However, I feel like it might blow up at any time - and my concern is race condition of the callback to finish before the function ends and the test is terminated - as this will be run in a separate thread.
My question now is if I'm on the right track unit testing the async function, or if anyone can help me get on the right track..? What would feel better is if I could ensure that the callback is triggered right away - and preferably on the same thread I guess? Can/Should it be done?
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);
}
You could use a ManualResetEvent to synchronize your threads.
In the following example, the test thread will block on the call to completion.WaitOne()
.The callback for the async calculation stores the result and then signals the event by calling 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);
// }
这篇关于单元测试异步函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!