首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在ViewModel构造函数中异步枚举智能卡终端

在ViewModel构造函数中异步枚举智能卡终端
EN

Code Review用户
提问于 2016-03-26 11:36:05
回答 1查看 166关注 0票数 6

前几天,我开始了一个项目,并开始沿着使用异步的道路前进,并等待它,因为我以前从未使用过它,这似乎是一个很好的匹配。我正在制作的应用程序是一个使用MVVM的WPF解决方案。它将从智能卡中读取并显示保存到智能卡中的信息。它是演示/概念的一部分。现在,视图几乎什么都没有,所以我不打算展示它。测试和ViewModel是感兴趣的。所以,让我从考试开始,因为这是我第一次在脑子里问这个问题的地方。我使用的是NUnit 3.2和Moq4.2

MainWindowViewModelTests.cs

代码语言:javascript
复制
[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

代码语言:javascript
复制
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和BaseViewModel

省略了,因为它只是IDataErrorInfo的包装器,而BaseViewModel只是INotifyPropertyChanged的包装器

ITerminal/ITerminalFactory

代码语言:javascript
复制
public interface ITerminal : System.IDisposable
{
    string Name { get; }

    IChippedCard Connect();
    void Disconnect();
}
public interface ITerminalFactory
{
    Task<IEnumerable<ITerminal>> EnumerateTerminals();
}

这就是我现在的处境。我以前从未使用过异步和等待。我以前从未使用过任务作为返回方法,我总是希望使用最佳实践,所以这就是我在这里的原因。所以请让我知道你的想法/担心/建议/想法。

EN

回答 1

Code Review用户

发布于 2016-03-29 06:09:16

我看到您在测试异步/等待中使用,所以您可以以同样的方式执行GetClassUnderTest。返回一个任务并异步地等待它。

此外,您还可以将内部while和Thread.Sleep更改为其他内容。我会将PopulateTerminals更改为public (除了Terminals集合之外,它不改变任何内部状态,您可以在验证是否已填充的填充之前进行检查),然后从测试调用中进行检查。

我认为将其公开是最好的方法,但是如果您不愿意这样做,可以考虑使用一些信号来表示关于IsLoading的信息,而不是做一些无用的事情。

我不喜欢EnumerateTerminals返回IEnumerable<ITerminal>。我不知道枚举的逻辑是什么,但是我想您从那里返回列表,所以也将返回值更改为Task<List<ITerminal>>。如果您确实返回IEnumerable,这可能是一个问题,因为懒惰。

在VM中,为什么您的集合是ICollection而不是ObservableCollection?如果您出于某种原因(排序)故意这样做,这是可以的,但如果不更改为具体类型(它更清晰,避免转换)。

例如,可以将ITerminalFactory作为属性注入VM,然后不需要传递它来填充集合。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/123931

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档