![](/img/trans.png)
[英]How do I write a Unit test for a RelayCommand that contains an Async Service Call?
[英]How to unit test RelayCommand that Executes an async method?
由於沒有RelayCommandAsync(至少我不知道),因此如何測試這種情況。 例如:
public RelayCommand LoadJobCommand
{
get
{
return this.loadJobCommand ?? (
this.loadJobCommand =
new RelayCommand(
this.ExecuteLoadJobCommandAsync));
}
}
private async void ExecuteLoadJobCommandAsync()
{
await GetData(...);
}
測試:
vm.LoadJobCommand.Execute()
Assert.IsTrue(vm.Jobs.Count > 0)
這實際上取決於您要測試的內容:
RelayCommand
是否正確連接並調用您的async
方法? 要么
Async
方法邏輯是否正確? 1.a使用外部依賴項進行驗證
根據我的個人經驗,最簡單的方法是測試觸發器是否正確連接以執行命令,然后測試您的類是否與預期的某個地方與另一個外部類進行了交互。 例如
private async void ExecuteLoadJobCommandAsync()
{
await GetData(...);
}
private async void GetData(...)
{
var data = await _repo.GetData();
Jobs.Add(data);
}
測試回購被調用相當容易。
public void TestUsingExternalDependency()
{
_repo.Setup(r => r.GetData())
.Returns(Task.Run(() => 5))
.Verifiable();
_vm.LoadJobCommand.Execute(null);
_repo.VerifyAll();
}
有時,我什至這樣做,以便它不會嘗試處理所有內容:
[Test]
public void TestUsingExternalDependency()
{
_repo.Setup(r => r.GetData())
.Returns(() => { throw new Exception("TEST"); })
.Verifiable();
try
{
_vm.LoadJobCommand.Execute(null);
}
catch (Exception e)
{
e.Message.Should().Be("TEST");
}
_repo.VerifyAll();
}
1.b使用調度程序
另一種選擇是使用調度程序,並使用該調度程序來調度任務。
public interface IScheduler
{
void Execute(Action action);
}
// Injected when not under test
public class ThreadPoolScheduler : IScheduler
{
public void Execute(Action action)
{
Task.Run(action);
}
}
// Used for testing
public class ImmediateScheduler : IScheduler
{
public void Execute(Action action)
{
action();
}
}
然后在您的ViewModel中
public ViewModelUnderTest(IRepository repo, IScheduler scheduler)
{
_repo = repo;
_scheduler = scheduler;
LoadJobCommand = new RelayCommand(ExecuteLoadJobCommandAsync);
}
private void ExecuteLoadJobCommandAsync()
{
_scheduler.Execute(GetData);
}
private void GetData()
{
var a = _repo.GetData().Result;
Jobs.Add(a);
}
和你的測試
[Test]
public void TestUsingScheduler()
{
_repo.Setup(r => r.GetData()).Returns(Task.Run(() => 2));
_vm = new ViewModelUnderTest(_repo.Object, new ImmediateScheduler());
_vm.LoadJobCommand.Execute(null);
_vm.Jobs.Should().NotBeEmpty();
}
GetData
邏輯 如果您要測試獲取GetData()
邏輯甚至ExecuteLoadJobCommandAsync()
邏輯。 然后,您絕對應該將要測試的方法設置為Internal,並將您的組件標記為InternalsVisibleTo
以便可以直接從測試類中調用這些方法。
我沒有使用async / await,但是過去遇到了類似的問題。 我所處的情況是其內部稱為Task.Run(
)的方法,並且單元測試正在驗證ViewModel是否以正確的次數和正確的參數調用服務。
解決此問題的方法是,我們使用ManualResetEventSlim
調用了服務的Mock,然后單元測試在繼續之前等待該重置事件被調用。
[TestMethod]
public void EXAMPLE()
{
using (var container = new UnityAutoMoqContainer())
{
//(SNIP)
var serviceMock = container.GetMock<ITreatmentPlanService>();
var resetEvent = new ManualResetEventSlim();
serviceMock.Setup(x=>x.GetSinglePatientViewTable(dateWindow, currentPatient, false))
.Returns(() =>
{
resetEvent.Set();
return new ObservableCollection<SinglePatientViewDataRow>();
});
var viewModel = container.Resolve<SinglePatientViewModel>();
//(SNIP)
viewModel.PatientsHadTPClosed(guids, Guid.NewGuid());
waited = resetEvent.Wait(timeout);
if(!waited)
Assert.Fail("GetSinglePatientViewTable was not called within the timeout of {0} ms", timeout);
//(SNIP)
serviceMock.Verify(x => x.GetSinglePatientViewTable(dateWindow, currentPatient, false), Times.Once);
}
}
這種方法對您是否有效,取決於您的單元測試實際正在測試什么。 因為您檢查了Assert.IsTrue(vm.Jobs.Count > 0)
所以看起來您有其他邏輯正在await GetData(...);
之后執行await GetData(...);
致電,因此這可能不適用於您當前的問題。 但是,這對於您需要為視圖模型編寫的其他單元測試可能會有所幫助。
為什么不使用測試覆蓋GetData(...)方法? 我在測試中繼命令時沒有任何意義
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.