【问题标题】:Testing a Normal C# Function with a Task Inside It测试一个包含任务的普通 C# 函数
【发布时间】:2021-04-09 03:38:36
【问题描述】:

我有一个不是我写的类,我需要测试它的私有字段。 (不是我的选择。)

public static class ArrayPool<T>
{
    public static void Return(T[] array)
    {
        PushTask = Task.Run(() =>
        {
            var bucketIndex = (int)Math.Log(array.Length, 2.0) - 6;
            Buckets[bucketIndex].Push(array);
        });
    }

    static ArrayPool()
    {
        Buckets = new List<ConcurrentStack<T[]>>(25);
        for (int i = 0; i < 25; ++i)
            Buckets.Add(new ConcurrentStack<T[]>());
    }

    private static readonly List<ConcurrentStack<T[]>> Buckets;
    public static Task PushTask; // I added this field.
}

我为这个类写了以下测试:

[TestClass]
public class ArrayPoolTesting
{
    [TestMethod]
    public void ArrayPool_Return()
    {
        // Act
        ArrayPool<int>.Return(new int[268435456]);

        // Assert
        if (ArrayPool<int>.PushTask.Wait(100))
        {
            PrivateType pt = new PrivateType(typeof(ArrayPool<int>));
            var buckets = (List<ConcurrentStack<int[]>>)pt.GetStaticField("Buckets");
            Assert.AreEqual(25, buckets.Count);
            Assert.AreEqual(1, buckets[22].Count); 
        }
    }
}

但是,它每次都失败。它在测试的if条件中抛出了一个空引用异常,因为ArrayPool&lt;int&gt;.PushTasknull。 (实际上,它在我的工作计算机上每次都失败。在我编写此精简版本的家用计算机上,它每次都有效。但我已经梳理了这两个程序,我相信它们本质上是相同的。)我不想修改原来的类,但我确实添加了 public PushTask 字段,希望使类可测试。

我明白为什么它失败了。在方法中的任务完成并将其值分配给Task 字段之前,它会到达测试中的if 条件。增加Wait时间无济于事。

  • 有什么方法可以挽救这种方法吗?
  • 有没有更好的方法来解决这个问题?

我从调试中知道Return 方法工作正常,但我无法对其进行测试,因为断言在Task 字段被初始化之前运行。

【问题讨论】:

  • 原来的Return 方法是使用Task 还是你添加的?
  • 原来使用的是任务。我添加了 PushTask 字段和 Task.Run 返回值的分配给它。
  • 如果可以的话,重构代码并一路异步。或一路同步。混合它们只是自找麻烦。
  • 但无论如何都要让测试异步并等待任务。
  • 仅供参考谁曾经写过这个方法。 ArrayPool 是一个非常糟糕的名字。此外,您应该测试行为而不是实现和内部状态。最后,这整个类是不确定的并且不是线程安全的,并且无论测试如何,将来都会被某人破坏

标签: c# unit-testing task


【解决方案1】:

简单

最简单的方法似乎是让Return 不使用Task.Run

不那么简单

如果这是不可能的,那么下一个最好的想法是使 Return 成为异步方法。

这不应该改变现有代码与方法交互的方式,因为它返回的是 void。 (顺便说一句。这也应该改变,所有来电者都应该await它)。

public static Task Return(T[] array)
{
   return Task.Run(() =>
   {
            ...
   });
}

那你就可以在测试中等待了。

[TestMethod]
public async Task ArrayPool_Return() <--- Added 'async Task'
{
   // Act
   await ArrayPool<int>.Return(new int[268435456]);

   // Assert
   PrivateType pt = new PrivateType(typeof(ArrayPool<int>));
   ...

即使你坚持PushTask 的想法,你仍然应该await 它,而不是Wait(100)

终于

【讨论】:

    猜你喜欢
    • 2017-06-05
    • 2018-04-16
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 2018-10-05
    • 1970-01-01
    • 2021-02-06
    • 2015-08-21
    相关资源
    最近更新 更多