C# 可测试异步操作的合适模式?
我很难找到一个简单、灵活的模式来允许我在ViewModels中编写代码,这些代码在运行时异步运行,但在测试时同步运行。这就是我想到的——有人有什么建议吗?这条路走下去好吗?有更好的现有模式吗 LongRunningCall定义:C# 可测试异步操作的合适模式?,c#,unit-testing,design-patterns,asynchronous,C#,Unit Testing,Design Patterns,Asynchronous,我很难找到一个简单、灵活的模式来允许我在ViewModels中编写代码,这些代码在运行时异步运行,但在测试时同步运行。这就是我想到的——有人有什么建议吗?这条路走下去好吗?有更好的现有模式吗 LongRunningCall定义: public class LongRunningCall { public Action ExecuteAction { get; set; } public Action PostExecuteAction { get; set; } pub
public class LongRunningCall
{
public Action ExecuteAction { get; set; }
public Action PostExecuteAction { get; set; }
public LongRunningCall(Action executeAction = null, Action postExecuteAction = null)
{
ExecuteAction = executeAction;
PostExecuteAction = postExecuteAction;
}
public void Execute(Action<Exception> onError)
{
try
{
ExecuteAction();
PostExecuteAction();
}
catch (Exception ex)
{
if (onError == null)
throw;
onError(ex);
}
}
public void ExecuteAsync(TaskScheduler scheduler, Action<Exception> onError)
{
var executeTask = Task.Factory.StartNew(ExecuteAction);
var postExecuteTask = executeTask.ContinueWith((t) =>
{
if (t.Exception != null)
throw t.Exception;
PostExecuteAction();
}, scheduler);
if (onError != null)
postExecuteTask.ContinueWith((t) => { onError(t.Exception); });
}
}
var continueCall = new LongRunningCall(continueCommand_Execute, continueCommand_PostExecute);
if (svc.IsAsyncRequired)
continueCall.ExecuteAsync(TaskScheduler.FromCurrentSynchronizationContext(), continueCommand_Error);
else
continueCall.Execute(continueCommand_Error);
唯一真正的先决条件是,您需要在运行时知道是否应该使用异步/同步。当我运行我的单元测试时,我发送一个模拟,告诉我的代码同步运行,当应用程序实际运行时,默认为true
反馈?考虑更改
ExecuteAsync
,使其返回任务
:
public Task ExecuteAsync(TaskScheduler scheduler, Action<Exception> onError)
但在单元测试中,我会等待任务实际完成:
var task = longRunningCall.ExecuteAsync(
TaskScheduler.FromCurrentSynchronizationContext(),
continueCommand_Error);
task.Wait();
我更愿意将关于是同步还是异步执行代码的决定封装在一个单独的类中,该类可以在如下接口后面进行抽象:
public interface ITaskExecuter
{
void ScheduleTask(
Action executeAction,
Action postExecuteAction,
Action<Exception> onException);
}
在调用类中没有用于测试和生产的单独代码路径
您可以选择编写以下测试:
- 只需检查是否已将正确的操作传递给任务执行器,或者
- 配置任务执行器以同步执行操作,并 测试所需结果,或
- 两者都做
IWorker
界面,使用DoWork(Func)
方法
然后我创建了两个派生类,一个是“AsyncWorker”,一个是“SyncWorker”。SyncWorker只执行传入的Func
(同步),而“AsyncWorker”是BackgroundWorker
的包装器,它将传入的Func
发送给BackgroundWorker进行异步处理
然后,我将ViewModel更改为传入一个IWorker
。这会将依赖项解析移出ViewModel,因此可以使用Dep.Inj。实用工具(我使用Unity和构造函数注入)
因为我使用Unity,所以在我的单元测试配置中,我将IWorker
映射到SyncWorker
,并在生产中将IWorker
映射到AsyncWorker
希望这有意义。。。我知道如果我手头有代码会更容易…这与我在回答中建议的解决方案相同。我将如何修改代码以选择性地获取参数?我是否必须使用ITaskExecutor和ScheduleTask复制ITaskExecutor及其实现(Func executeAction、Action postExecuteAction等…?有没有办法让我的TaskExecutor足够通用,可以像Task那样处理任何参数集,而不必做那么多定义?我原以为您会将execute Action和post executeAction作为匿名委托提供,其中已经包含任何参数,并且它们都是
Action
类型。例如taskExecuter.ScheduleTask(()=>MyTask(1,2.3f,“随便”),()=>MyPostTask(true,false,“等等”),MyExceptionHandler(E));
public interface ITaskExecuter
{
void ScheduleTask(
Action executeAction,
Action postExecuteAction,
Action<Exception> onException);
}
taskExecuter.ScheduleTask(
continueCommand_Execute,
continueCommand_PostExecute,
continueCommand_Error);