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