如何使用NUnit测试异步单元测试的竞争条件?

时间:2017-10-15 06:58:10

标签: c# multithreading unit-testing

这是我尝试测试比赛条件的代码, 我有遗留代码,我可以在一定程度上改变。 在我的main函数中,我有一个函数,它返回一个我们等待的Task和另一个不能被阻止的函数,因为它在不同的线程上运行。

在原始代码中,我们不关心结果,我们只是初始化一些类,我测试ViewModel不是null。

在我给出的示例代码中,我更改了代码以举例说明竞争条件的问题。 目前,我的测试将失败。 如何正确测试我的比赛条件

 [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            Myclass tempClass = new Myclass();
            tempClass.CreateImage();
            Assert.AreEqual(3, tempClass.Sum);
        }
    }

    public class Myclass
    {
        public int Sum;

        public Myclass()
        {
            Sum = 0;
        }
        public async Task CreateImage()
        {
            await InternalFunction();
            await Task.Run(() => Physics());
        }

        public async Task InternalFunction()
        {
            await Task.Run(() =>
            {
                Math();
            });
        }

        public void Math()
        {
            Sum += 1;
        }

        public void Physics()
        {
            Sum += 2;
        }
    }

如果我运行测试,sum属性将为1,而不是预期的3。如何更改代码以便能够执行CreateImage()函数的所有流程,并且只有在完成所有线程后,我想断言TempClass的结果/内容。

2 个答案:

答案 0 :(得分:2)

CreateImage方法是异步的,但是您的单元测试方法不是,因此您的测试可能会在CreateImage完成之前完成。

您没有指定您正在使用的NUnit版本,但以下版本适用于最新版本的NUnit 3

[TestFixture]
public class UnitTest1
{
    [Test]
    public async Task TestMethod1()
    {
        Myclass tempClass = new Myclass();
        await tempClass.CreateImage();
        Assert.AreEqual(3, tempClass.Sum);
    }
}

当然,这假设MyClass是线程安全的 - 如果您的实际代码使用多个线程同时修改MyClass实例的状态,那么您可能还需要考虑使用同步机制比如BCL内置的其中一个 - https://docs.microsoft.com/en-us/dotnet/standard/threading/synchronizing-data-for-multithreading

另外,您可以考虑使用Stephen Cleary开发的一些异步友好助手 - https://github.com/StephenCleary/AsyncEx

答案 1 :(得分:2)

您还必须等待测试中的代码,否则Aseert将在代码完成之前进行:

        [TestMethod]
        public async Task TestMethod1()
        {
            Myclass tempClass = new Myclass();
            await tempClass.CreateImage();
            Assert.AreEqual(3, tempClass.Sum);
        }

但对我来说,这段代码有一些pifalls。 Task.Run中有很多包装同步代码,它现在不会带来任何好处。此外Sum不是线程安全的,所以如果在任何时间线程它将被线程访问,则会发生并发的坏事。