编程语言
首页 > 编程语言> > MVVM:异步事件处理程序的单元测试

MVVM:异步事件处理程序的单元测试

作者:互联网

我有一个带有异步任务的viewModel.我现在不测试它.

public class MyViewModel : BindableBase
{
    public MyViewModel()
    {
        this.PropertyChanged += MyViewModel_PropertyChanged;
    }

    private void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        Action action = async () => await DoSomething();
        action();
    }

    public const string BeforeKey = "before";
    public const string AfterKey = "After";

    public string Status { get; private set; } = BeforeKey;

    public async Task DoSomething()
    {
        await Task.Delay(3000);
        Status = AfterKey;
    }

    string bindagleProp;
    public string BindagleProp
    {
        get { return bindagleProp; }
        set { SetProperty(ref bindagleProp, value); }
    }
}

这是我的测试:

[TestMethod]
public async Task TestMyViewModel()
{
    MyViewModel viewModel = new MyViewModel();
    Assert.AreEqual(viewModel.Status, MyViewModel.BeforeKey, "before check");

    viewModel.BindagleProp = "abc";
    Assert.AreEqual(viewModel.Status, MyViewModel.AfterKey, "after check");
}

测试失败,因为它没有等待任务完成.
我不想在单元测试中使用Task.Delay,因为它不安全. DoSomething方法的持续时间未知.

感谢您的任何帮助.

编辑:

实际上,该问题并非特定于MVVM,而是所有异步事件处理程序.
例如:

// class with some logic, can be UI or whatever.
public class MyClassA
{
    Size size;

    public Size Size
    {
        get { return size; }
        set
        {
            size = value;
            SizeChanged?.Invoke(this, EventArgs.Empty);
        }
    }

    public event EventHandler SizeChanged;
}

// this class uses the MyClassA class.
public class MyCunsomerClass
{
    readonly MyClassA myClassA = new MyClassA();

    public MyCunsomerClass()
    {
        myClassA.SizeChanged += MyClassA_SizeChanged;
    }

    public string Status { get; private set; } = "BEFORE";

    private async void MyClassA_SizeChanged(object sender, EventArgs e)
    {
        await LongRunningTaskAsync();
        Status = "AFTER";
    }

    public async Task LongRunningTaskAsync()
    {
        await Task.Delay(3000);
        ///await XYZ....;
    }

    public void SetSize()
    {
        myClassA.Size = new Size(20, 30);
    }
}

现在,我要测试一下:

    [TestMethod]
    public void TestMyClass()
    {
        var cunsomerClass = new MyCunsomerClass();
        cunsomerClass.SetSize();
        Assert.AreEqual(cunsomerClass.Status, "AFTER");
    }

测试失败.

解决方法:

好吧,首先,我将工人移到另一个类并为其建立接口.这样,当我运行测试时,我可以注入另一个工人!

public class MyViewModel : BindableBase
{
    private IWorker _worker;

    private readonly DataHolder _data = new DataHolder(){Test = DataHolder.BeforeKey};
    public string Status { get { return _data.Status; } }

    public MyViewModel(IWorker worker = null)
    {
        _worker = worker;
        if (_worker == null)
        {
            _worker = new Worker();
        }

        this.PropertyChanged += MyViewModel_PropertyChanged;
    }

    private void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {

        Action action = async () => await _worker.DoSomething(_data);
        action();
    }


    string bindagleProp;
    public string BindagleProp
    {
        get { return bindagleProp; }
        set { SetProperty(ref bindagleProp, value); }
    }
}

public class DataHolder
{
    public const string BeforeKey = "before";
    public const string AfterKey = "After";

    public string Status;
}

public interface IWorker
{
    Task DoSomething(DataHolder data);
}

public class Worker : IWorker
{
    public async Task DoSomething(DataHolder data)
    {
        await Task.Delay(3000);
        data.Status = DataHolder.AfterKey;
    }
}

现在注入代码如下所示:

[TestMethod]
public async Task TestMyViewModel()
{
    TestWorker w = new TestWorker();

    MyViewModel viewModel = new MyViewModel(w);
    Assert.AreEqual(viewModel.Status, DataHolder.BeforeKey, "before check");

    viewModel.BindagleProp = "abc";
    Assert.AreEqual(viewModel.Status, DataHolder.AfterKey, "after check");
}

public class TestWorker : IWorker
{
    public Task DoSomething(DataHolder data)
    {
        data.Status = DataHolder.BeforeKey;
        return null; //you maybe should return something else here...
    }
}

标签:unit-testing,asynchronous,async-await,c,mvvm
来源: https://codeday.me/bug/20191119/2036459.html