2

考虑以下代码:

public interface IBar
{
    Task<IEnumerable<string>> GetStringsAsync();
}

public class Foo
{
    public Foo(IBar bar, IList<string> initial)
    {
        MyCollection = new ObservableCollection<string>();
        if (initial == null || !initial.Any())
            AddContent(bar);
        else
            MyCollection.AddRange(initial);
    }

    public ObservableCollection<string> MyCollection { get; private set; }

    public void AddContent(IBar bar)
    {
        var cancel = new CancellationTokenSource();
        bar.GetStringsAsync().ContinueWith(
            task => MyCollection.AddRange(task.Result),
            cancel,
            TaskContinuationOptions.NotOnCancel,
            TaskScheduler.FromCurrentSynchronizationContext());
    }
}

如何对 Foo.AddContent 方法进行单元测试?我想测试我的模拟 IBar 提供的字符串实际上是否被添加到集合中,但是断言总是在任务更新集合之前被调用。

我正在使用 .NET 4.5.2。我的第一选择是在 AddContent 中使用asyncand await,但是因为在构造函数中使用了该方法,所以我认为最好避免这种情况。我需要一些可以启动数据异步加载但不会等待它完成的东西。

欢迎提出关于如何重写 AddContent 的建议,但我已经尝试了很多东西,这是唯一一个运行良好的东西,所以我真正想要的是一种测试它的方法。

4

2 回答 2

1

更新 2

使用此处找到的 Stephen Cleary 的异步初始化模式。

http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html

更新

由于问题已经改变,现在的要求是让构造函数接受变量IBarIBar根据传递给构造函数的变量的硬性要求,我建议以下内容:

public class Foo
{
    public Foo(IBar bar)
    {
        MyCollection = new ObservableCollection<string>();
        MyCollection.AddRange(bar.GetStringsAsync().Result));
    }

    public ObservableCollection<string> MyCollection { get; private set; }

    public async Task AddContent(IBar bar)
    {
        MyCollection.AddRange(await bar.GetStringsAsync());
    }
}

注意:该public方法仍然使用首选机制async / await,但构造函数只是调用.Result. 这是构造函数中的阻塞调用,也是一种非常糟糕的做法。这很容易被认为是你永远不应该做的事情......

我强烈建议您的构造函数采用初始字符串,而不是这样,(特别是考虑到它仅用于它返回的字符串!):

public class Foo
{
    public Foo(IEnumerable<string> strings)
    {
        MyCollection = new ObservableCollection<string>();
        MyCollection.AddRange(strings);
    }

    public ObservableCollection<string> MyCollection { get; private set; }

    public async Task AddContent(IBar bar)
    {
        MyCollection.AddRange(await bar.GetStringsAsync());
    }
}

用法

[TestMethod]
public async Task Test()
{
    IBar bar = GetMockedBarImpl();
    var sut = new Foo(await bar.GetStringsAsync());        

    Assert.IsTrue(sut.MyCollection.Any());
    // TODO: Add asserts for known strings in collection...
}
于 2015-12-14T17:03:19.650 回答
-1

看起来有问题的代码存在继承竞争条件。我会等待GetStringAsync完成并分配给MyCollection.

public void AddContent(IBar bar)
{
    var cancel = new CancellationTokenSource();
    var result = bar.GetStringsAsync().ContinueWith(
        task => task.Result,
        cancel,
        TaskContinuationOptions.NotOnCancel,
        TaskScheduler.FromCurrentSynchronizationContext());

   MyCollection.AddRange(result.Result);
}

或者干脆

public void AddContent(IBar bar)
{
    var result = bar.GetStringsAsync().Result;
    MyCollection.AddRange(result);
}
于 2015-12-14T17:00:39.300 回答