运行多线程 nunit 测试的最佳方法

发布于 2024-07-09 16:26:40 字数 372 浏览 8 评论 0 原文

我目前正在尝试找到一个解决方案,如果测试方法生成的线程中发生异常,如何确保测试失败。

我不想在单元测试中开始讨论多个线程。 => "单元测试".Replace("单元","集成");

我已经在几个论坛上阅读了很多帖子,我知道 CrossThreadTestRunner,但我正在寻找一个集成到 nunit 中的解决方案,并且不需要重写大量测试。

I currently trying to find a solution, how to ensure that a test fails if an exception occurs in a thread which is spawn by the test method.

I DON'T want to start a discussion about more than one thread in a unit test. => "unit test".Replace("unit","integration");

I already read a lot of threads in several forums, I know about CrossThreadTestRunner, but I'm searching for a solution whichs integrates into nunit, and does not require to rewrite a lot of tests.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

深海夜未眠 2024-07-16 16:26:41

非测试线程(即其他生成的线程)上的异常不会导致测试失败的原因是 NUnit 默认配置为使用 legacyUnhandledExceptionPolicy 这是一个.Net进程级别设置,可以通过app.config应用,即:

<legacyUnhandledExceptionPolicy enabled="1"/>

启用此设置(即将其设置为“1”)会导致异常不会在主线程上发生的情况将被忽略。

我写了一篇文章,更详细地介绍了 ReSharper 测试运行程序的这个问题,但它同样适用于 NUnit 测试运行程序:
https://web.archive.org/web/20101006004301/http://gojisoft.com/blog/2010/05/14/resharper-test-runner-hidden-thread-exceptions/)

ReSharper 测试运行程序 - 隐藏线程异常

作者:蒂姆·劳埃德,2010 年 5 月 14 日下午 4:31

我们在 GojiSoft 使用 ReSharper 测试运行程序来运行 NUnit 测试
从 Visual Studio 中。 这是一个很棒的测试运行程序,但无法运行
与可能发生异常的多线程组件很好地配合
非测试线程。 非测试线程上未处理的异常被隐藏
以及应该失败的测试,而不是通过。

...

问题在于 ReSharper 测试运行程序是
配置为与 .Net 1.0 和 1.1 应用程序的行为方式相同,其中
非主线程上未处理的异常被吞掉。 情况
从 .Net 2.0 进行了改进,其中所有未处理的异常都会使
过程。 然而,微软不得不允许现有的.Net 1.0和1.1
应用程序可以选择在新的 .Net 框架上像以前一样运行。 他们
引入了 app.config 设置:legacyUnhandledExceptionPolicy。

ReSharper 测试运行程序默认配置为使用 .Net 1.0
和1.1策略一样,所以如果存在未处理的非测试线程异常
它不会冒泡并导致测试失败——测试通过,
但我们却得到了误报。

如果非测试线程上未处理的异常导致测试失败,则
必须更新 ReSharper 测试运行程序的 app.config。

...

  • 通过编辑legacyUnhandledExceptionPolicy关闭旧版未处理异常策略:

  • 现在,当多线程测试在非测试线程上引发异常时,它们会按预期失败:

  • 买家要小心……

    对此有一个警告。 非测试线程上的异常现在将
    扁平化测试运行程序,并且测试套件执行将在以下情况下停止:
    它们发生了。 这与失败的正常测试运行形成对比
    测试被标记为失败,测试运行程序将继续。 ...

    The reason that exceptions on non-test threads (i.e. other spawned threads) do not cause tests to fail is that NUnit is configured by default to use legacyUnhandledExceptionPolicy which is a .Net process level setting which can be applied via app.config, i.e.:

    <legacyUnhandledExceptionPolicy enabled="1"/>
    

    Enabling this setting (i.e. setting it to "1") causes exceptions which do not occur on the main thread to be ignored.

    I wrote an article which goes into more detail in reference to this issue with the ReSharper test runner, but it applies equally to the NUnit test runner:
    https://web.archive.org/web/20101006004301/http://gojisoft.com/blog/2010/05/14/resharper-test-runner-hidden-thread-exceptions/)

    ReSharper test runner – hidden thread exceptions

    By Tim Lloyd, May 14, 2010 4:31 pm

    We use the ReSharper test runner here at GojiSoft to run NUnit tests
    from within Visual Studio. It’s a great test runner, but doesn’t play
    nicely with multi-threaded components where exceptions may occur on
    non-test threads. Unhandled exceptions on non-test threads are hidden
    and tests which should fail, instead pass.

    ...

    The problem lies in the fact that the ReSharper test runner is
    configured to behave in the same way as .Net 1.0 and 1.1 apps where
    unhandled exceptions on non-main threads were swallowed. The situation
    improves from .Net 2.0 where all unhandled exceptions flatten the
    process. However, Microsoft had to allow existing .Net 1.0 and 1.1
    apps the option of behaving as before on new .Net frameworks. They
    introduced the app.config setting: legacyUnhandledExceptionPolicy.

    The ReSharper test runner is configured by default to use the .Net 1.0
    and 1.1 policy, so if there is an unhandled non-test thread exception
    it does not bubble up and cause the test to fail – the test passes,
    and we get a false positive instead.

    If unhandled exceptions on non-test threads should fail tests, the
    app.config for the ReSharper test runner has to be updated.

    ...

    1. Turn the legacy unhandled exception policy off by editing legacyUnhandledExceptionPolicy: <legacyUnhandledExceptionPolicy enabled="0" />

    2. Now multi-threaded tests fail as expected when they raise exceptions on non-test threads:

    Buyer beware…

    There is a caveat to this. Exceptions on non-test threads will now
    flattened the test runner and test suite execution will be halted when
    they happen. This is in contrast to normal test runs where failed
    tests are marked as failed, and the test runner continues. ...

    半岛未凉 2024-07-16 16:26:41

    我遇到了同样的问题,我的解决方案是捕获异常并增加异常计数器,因此 Test 方法只需断言异常计数器为 0 即可确认没有线程发生异常。

    删除特定环境内容后我的测试代码的摘录:

        const int MaxThreads = 25;
        const int MaxWait = 10;
        const int Iterations = 10;
        private readonly Random random=new Random();
        private static int startedThreads=MaxThreads ;
        private static int exceptions = 0;
    

    ...

    [Test]
        public void testclass()
        {
            // Create n threads, each of them will be reading configuration while another one cleans up
    
            Thread thread = new Thread(Method1)
            {
                IsBackground = true,
                Name = "MyThread0"
            };
    
            thread.Start();
            for (int i = 1; i < MaxThreads; i++)
            {
                thread = new Thread(Method2)
                {
                    IsBackground = true,
                    Name = string.Format("MyThread{0}", i)
                };
    
                thread.Start();
            }
    
            // wait for all of them to finish
            while (startedThreads > 0 && exceptions==0)
            {
                Thread.Sleep(MaxWait);
            }
            Assert.AreEqual(0, exceptions, "Expected no exceptions on threads");
        }
    
        private void Method1()
        {
            try
            {
                for (int i = 0; i < Iterations; i++)
                {
                // Stuff being tested
                    Thread.Sleep(random.Next(MaxWait));
                }
            }
            catch (Exception exception)
            {
                Console.Out.WriteLine("Ërror in Method1 Thread {0}", exception);
                exceptions++;
            }
            finally
            {
                startedThreads--;
            }
        }
    
        private void Method2()
        {
            try
            {
                for (int i = 0; i < Iterations; i++)
                {
                    // Stuff being tested
                    Thread.Sleep(random.Next(MaxWait));
                }
            }
            catch (Exception exception)
            {
                Console.Out.WriteLine("Ërror in Method2 Thread {0}", exception);
                exceptions++;
            }
            finally
            {
                startedThreads--;
            }
        }
    

    I just had the same problem, my solution is to catch the exception and increment an exception counter, so the Test method just have to assert the exception counter is 0 to confirm no thread got an exception.

    An extract of my test code once specific environment stuff is removed:

        const int MaxThreads = 25;
        const int MaxWait = 10;
        const int Iterations = 10;
        private readonly Random random=new Random();
        private static int startedThreads=MaxThreads ;
        private static int exceptions = 0;
    

    [Test]
        public void testclass()
        {
            // Create n threads, each of them will be reading configuration while another one cleans up
    
            Thread thread = new Thread(Method1)
            {
                IsBackground = true,
                Name = "MyThread0"
            };
    
            thread.Start();
            for (int i = 1; i < MaxThreads; i++)
            {
                thread = new Thread(Method2)
                {
                    IsBackground = true,
                    Name = string.Format("MyThread{0}", i)
                };
    
                thread.Start();
            }
    
            // wait for all of them to finish
            while (startedThreads > 0 && exceptions==0)
            {
                Thread.Sleep(MaxWait);
            }
            Assert.AreEqual(0, exceptions, "Expected no exceptions on threads");
        }
    
        private void Method1()
        {
            try
            {
                for (int i = 0; i < Iterations; i++)
                {
                // Stuff being tested
                    Thread.Sleep(random.Next(MaxWait));
                }
            }
            catch (Exception exception)
            {
                Console.Out.WriteLine("Ërror in Method1 Thread {0}", exception);
                exceptions++;
            }
            finally
            {
                startedThreads--;
            }
        }
    
        private void Method2()
        {
            try
            {
                for (int i = 0; i < Iterations; i++)
                {
                    // Stuff being tested
                    Thread.Sleep(random.Next(MaxWait));
                }
            }
            catch (Exception exception)
            {
                Console.Out.WriteLine("Ërror in Method2 Thread {0}", exception);
                exceptions++;
            }
            finally
            {
                startedThreads--;
            }
        }
    
    酸甜透明夹心 2024-07-16 16:26:41

    我通过为 nunit 创建一个插件解决了这个问题,它“安装”了一个 ITestDecorator。

    I solved the problem by creating an addin for nunit, which "installs" an ITestDecorator.

    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文