I started a project the other day and started going down the path of using async and await as I have never used it before and it seems like a good match. The app that I'm making is a WPF solution using MVVM. It is going to read from a smart card and show the information that was saved to the card. It is part of a demo/concept. As it stands right now the View has next to nothing in it so I'm not going to show it. The test and the ViewModel are of the interest. So let me begin with the test as it is where I first asked this question in my head. I'm using NUnit 3.2 and Moq 4.2
MainWindowViewModelTests.cs
[TestFixture]
public class MainWindowViewModelTests
{
private Mock<ITerminalFactory> _factory;
[SetUp]
public void Setup()
{
_factory = new Mock<ITerminalFactory>();
}
[Test]
public void WhenNoTerminalIsConnected_ErrorMessageIndicatesNoTerminal()
{
_factory.Setup(f => f.EnumerateTerminals()).Returns(async ()=>
{
await Task.Yield();
return new ITerminal[0];
});
var target = GetClassUnderTest();
Assert.That(target.Error, Is.EqualTo("No Terminals"));
}
[Test]
public void WhenAtleastOneTerminalIsConnected_NoErrorMessages()
{
_factory.Setup(f => f.EnumerateTerminals()).Returns(async () =>
{
await Task.Yield();
return new ITerminal[] { Mock.Of<ITerminal>()};
});
var target = GetClassUnderTest();
Assert.That(target.Error, Is.Null.Or.Empty);
Assert.That(target.Terminals.Count, Is.EqualTo(1));
}
private MainWindowViewModel GetClassUnderTest()
{
var target = new MainWindowViewModel(_factory.Object);
while (!target.IsLoaded)
{
System.Threading.Thread.Sleep(1);
}
return target;
}
}
MainWindowViewModel.cs
public class MainWindowViewModel : HandleErrorsViewModel
{
public ICollection<ITerminal> Terminals { get; }
public bool IsLoaded { get; private set; }
public MainWindowViewModel(ITerminalFactory terminalFactory)
{
Terminals = new ObservableCollection<ITerminal>();
PopulateTerminals(terminalFactory);
}
private async void PopulateTerminals(ITerminalFactory terminalFactory)
{
IEnumerable<ITerminal> terminals = await terminalFactory.EnumerateTerminals();
terminals.ToList().ForEach(Terminals.Add);
if (Terminals.Count == 0)
Error = "No Terminals";
IsLoaded = true;
}
}
HandleErrorsViewModel.cs and BaseViewModel
omitted as it is just a wrapper around IDataErrorInfo and a BaseViewModel which is just a wrapper around INotifyPropertyChanged
ITerminal/ITerminalFactory
public interface ITerminal : System.IDisposable
{
string Name { get; }
IChippedCard Connect();
void Disconnect();
}
public interface ITerminalFactory
{
Task<IEnumerable<ITerminal>> EnumerateTerminals();
}
And that is as far as I am right now. I've neve used async and await before. I've never used a Task as a return method before, and I always desire to use the best practices so that is why I am here. So please fire away and let me know thoughts/concerns/tips/ideas.