簡體   English   中英

如何對執行異步方法的RelayCommand進行單元測試?

[英]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)

這實際上取決於您要測試的內容:

  1. 測試RelayCommand是否正確連接並調用您的async方法?

要么

  1. 測試Async方法邏輯是否正確?

1.測試RelayCommand觸發器

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();
    }

2.測試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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM