2017-08-25 38 views
0

一個異步指揮自動化測試我有一個異步Command類,如下所示:在MVVM

public AsyncDelegateCommand(Func<Task> execute, Func<bool> canExecute) 
{ 
    this.execute = execute; 
    this.canExecute = canExecute; 
} 

public virtual bool CanExecute(object parameter) 
{ 
    if(executing) 
     return false; 
    if(canExecute == null) 
     return true; 
    return canExecute(); 
} 

public async void Execute(object parameter) // Notice "async void" 
{ 
    executing = true; 
    CommandManager.InvalidateRequerySuggested(); 
    if(parameter != null && executeWithParameter != null) 
     await executeWithParameter(parameter); 
    else if(execute != null) 
     await execute(); 
    executing = false; 
    CommandManager.InvalidateRequerySuggested(); 
} 

,它被稱爲像這樣:

FindProductCommand = new AsyncDelegateCommand(TryToFindProduct,() => CanFindProduct() && connector.HasConnection); 

private async Task TryToFindProduct() 
{ 
    //code 
} 

當我的單元測試,它工作得好,因爲我立即從任務返回。

但是,在編寫我的集成測試時,我遇到了麻煩。我無法等待Execute,因爲它是void,我無法將其更改爲Task。我最終這樣做::/

findProductViewModel.FindProductCommand.Execute(null); 
Thread.Sleep(2000); 

var informationViewModel = findProductViewModel.ProductViewModel.ProductInformationViewModel; 

Assert.AreEqual("AFG00", informationViewModel.ProductGroup); 

這個測試有更好的解決方案嗎?也許是依賴於實際需要多長時間的東西,並且不估計需要等待多久。

回答

2

您可以通過@StephenCleary指好的博客文章:https://msdn.microsoft.com/en-us/magazine/dn630647.aspx
async void一般是被避免,所以他引入了一個異步的命令一個新的接口(和它的基本實現):IAsyncCommand。該界面包含您可以在測試中等待的方法async Task ExecuteAsync(object parameter)

public interface IAsyncCommand : ICommand 
{ 
    Task ExecuteAsync(object parameter); 
} 

public abstract class AsyncCommandBase : IAsyncCommand 
{ 
    public abstract bool CanExecute(object parameter); 
    public abstract Task ExecuteAsync(object parameter); 

    public async void Execute(object parameter) 
    { 
    await ExecuteAsync(parameter); 
    } 

    public event EventHandler CanExecuteChanged 
    { 
    add { CommandManager.RequerySuggested += value; } 
    remove { CommandManager.RequerySuggested -= value; } 
    } 

    protected void RaiseCanExecuteChanged() 
    { 
    CommandManager.InvalidateRequerySuggested(); 
    } 
} 

這樣一個異步命令的簡單的實現應該是這樣的:

public class AsyncCommand : AsyncCommandBase 
{ 
    private readonly Func<Task> _command; 

    public AsyncCommand(Func<Task> command) 
    { 
    _command = command; 
    } 

    public override bool CanExecute(object parameter) 
    { 
    return true; 
    } 

    public override Task ExecuteAsync(object parameter) 
    { 
    return _command(); 
    } 
} 

但也有,你可以在鏈接的博客中發現更高級的變種。您可以在代碼中一直使用IAsyncCommand,因此您可以對其進行測試。您使用的MVVM框架也會很開心,因爲該接口基於ICommand

1

如果你有一個狀態來檢查,你應該只使用SpinWait.SpinUntil

它將比Thread.Sleep更可靠,因爲它可以讓你檢查,如果條件是繼續之前如此。

例如

findProductViewModel.FindProductCommand.Execute(null); 
SpinWait.SpinUntil(() => findProductViewModel.ProductViewModel.ProductInformationViewModel != null, 5000); 

var informationViewModel = findProductViewModel.ProductViewModel.ProductInformationViewModel; 

Assert.AreEqual("AFG00", informationViewModel.ProductGroup); 
+0

非常有用。下次我無法等待執行時,我可能會考慮這一點。儘管如此,我已經表明等待可能成爲可能。謝謝。 –

2

這個測試有更好的解決方案嗎?也許有些東西依賴於實際需要多長時間,並且不估計需要等多久。

當然:使命令等待並且await它。 asyncvoid方法是不好的做法,並且是只有意味着用於事件處理程序。

Mvvm.AsyncReactiveUI中有可用的等待命令,您可以使用或至少查看以供參考。

+0

謝謝。看起來你的鏈接指向了StephanCleary。這和@dymanoid建議的一樣。既然他提供了,我會給他接受的答案,但有一個upvote。 –