NUnit的:为什么不Assert.Throws< T>赶上我ArgumentNullException?上我、Throws、Assert、NUnit

2023-09-03 00:08:22 作者:与终

我重新张贴了这个问题尊敬的约翰飞碟双向先生,谁建议我设计一个简单的测试程序,它隔离,并演示了我遇到的问题,并重新发布问题的遗志。这个问题的前身是this 之一,所以请原谅我,如果这一切听起来很熟悉。您可以潜在地收集有关从一个这个问题的其他详细信息。

I am posting this question anew at the behest of the distinguished Mr. John Skeet, who suggested I devise a simple test program that isolates and demonstrates the issue I am encountering and repost the question. This question grew out of this one, so please forgive me if it all sounds very familiar. You can potentially glean extra details about this question from that one.

我遇到关于 Assert.Throws&LT的问题; T> 从NUnit的2.5.9。它将,有时无法赶上的扔在了TestDelegate调用的方法的任何的异常。我在下面的code可重复的方式牵制这种行为。 (尽管这可能,当然,这样的情况,失败在我的机器™。

The issue I am encountering regards Assert.Throws<T> from NUnit 2.5.9. It will, on occasion, fail to catch any exceptions thrown in the method invoked by the TestDelegate. I have pinned down this behavior in a reproducible manner in the code below. (Though this may, admittedly, be a case of Fails On My Machine™.

要重现错误,我创建了一个解决方案,两个C#DLL项目:

To reproduce the error, I've created a solution with two C# DLL projects:

在第一个包含一个类,用一个单一的公共方法。该方法是一个封装创建一个的SqlCommand ,填充它的参数,并调用所需的逻辑扩展方法的ExecuteScalar 就可以了。该项目包括任何其它引用。 第二个是包含有两种方法来测试,在第一DLL中的方法是否按预期方式工作的类。该项目引用了第一,并包括一个参考NUnit的框架。没有其他组件引用。 The first contains one class, with a single public method. That method is an extension method that encapsulates the logic required to create a SqlCommand, populate its parameters and invoke ExecuteScalar on it. This project includes no other references. The second contains a single class with two methods that test whether or not the method in the first DLL is working as expected. This project references the first, and includes a reference to the NUnit Framework. No other assemblies are referenced.

当我通过调试器的测试步骤,我注意以下事项:

When I step through the tests in the debugger, I observe the following:

Assert.Throws 正确地调用了的ExecuteScalar&LT; T&GT; 扩展方法。 的参数值是零,符合市场预期。 的ExecuteScalar&LT; T&GT; 测试其参数为空值 在调试器击中并执行包含的行抛出新ArgumentNullException(...)。 执行抛后,应用程序的控制并不立即转移到 Assert.Throws 。相反,它延续到下一行的ExecuteScalar&LT; T&GT; 只要下一行code执行一系列的调试器中断,并显示错误参数无效的例外是未处理由用户code。 Assert.Throws correctly invokes the ExecuteScalar<T> extension method. The parameter values are null, as expected. ExecuteScalar<T> tests its parameters for null values. The debugger does hit and execute the line containing throw new ArgumentNullException(...). After executing the throw, control of the application is not immediately transferred to Assert.Throws. Instead, it continues on the next line in ExecuteScalar<T>. As soon as the next line of code executes, the debugger breaks, and displays the error "Argument null exception was unhandled by user code."

该人士$ ​​C $ C,它使此行为是如下。

The source code that isolates this behavior is given below.

可拓方法

namespace NUnit_Anomaly
{
    using System;
    using System.Data;
    using System.Data.SqlClient;

    public static class Class1
    {
        public static T ExecuteScalar<T>(this SqlConnection connection, string sql)
        {
            if (connection == null)
            {
                throw new ArgumentNullException("connection");
            }

            if (sql == null)
            {
                throw new ArgumentNullException("sql");
            }

            using (var command = connection.CreateCommand())
            {
                command.CommandType = CommandType.Text;
                command.CommandText = sql;
                return (T)command.ExecuteScalar();
            }
        }
    }
}

测试用例

namespace NUnit_Tests
{
    using System;
    using System.Data.SqlClient;
    using System.Diagnostics;

    using NUnit.Framework;

    using NUnit_Anomaly;

    [TestFixture]
    public class NUnitAnomalyTest
    {

        [Test]
        public void ExecuteDataSetThrowsForNullConnection()
        {
            Assert.Throws<ArgumentNullException>(() => ((SqlConnection)null).ExecuteScalar<int>(null));
        }

        [Test]
        public void ExecuteDataSetThrowsForNullSql()
        {

            const string server = "MY-LOCAL-SQL-SERVER";
            const string instance = "staging";
            string connectionString = String.Format("Data Source={0};Initial Catalog={1};Integrated Security=True;",
                                                    server,
                                                    instance);

            using (var connection = new SqlConnection(connectionString))
            {
                Assert.Throws<ArgumentNullException>(() => connection.ExecuteScalar<int>(null));
            }
        }
    }
}

净效果是,测试失败时,他们不应该。尽我的理解, Assert.Throws&LT; T&GT; 应抓住我的异常,测试应该通过

The net effect is that the tests fail when they shouldn't. To the best of my understanding, Assert.Throws<T> should catch my exception and the test should pass.

更新的

UPDATE

我把汉斯的意见,并检查了例外对话框。我没有打破的抛出的例外情况,但我打破的未处理的用户的异常。显然,这就是为什么调试器闯入IDE时,抛出异常。清除该复选框解决了问题,而 Assert.Throws&LT; T&GT; 把它捡起来。不过,如果我没有这样做,我不能只preSS F5继续执行,或异常会成为的NullReferenceException

I took Hans' advice and checked the Exceptions dialog. I wasn't breaking on thrown exceptions, but I was breaking on unhandled user exceptions. Apparently, that's why the debugger breaks into the IDE when the exception is thrown. Clearing the checkbox fixed the problem, and Assert.Throws<T> picked it up. However, if I haven't done this, I can't just press F5 to continue execution, or the exception will become a NullReferenceException.

所以,现在的问题是:我可以在每个项目的基础上配置的异常中断?我只是想这样做,当我测试,而不是一般的。

So now the question is: Can I configure exception breaks on a per-project basis? I only want to do this when I'm testing, but not in general.

推荐答案

什么实际情况是, Assert.Throws 确实抓住你的例外,在第一次机会的例外,但是Visual Studio中停止反正。你可以只pressing F5检查这一点; Visual Studio将愉快地继续执行。

What actually happens is that Assert.Throws does catch your exception, however Visual Studio stops on the first-chance exception anyway. You can check this by just pressing F5; Visual Studio will happily carry on executing.

由于异常助手会告诉你,除了是未处理的按用户code 。因此,我们知道Visual Studio中不考虑NUnit的是用户code由于某种原因。

As the exception helper tells you, the exception was unhandled by user code. So we know that Visual Studio doesn’t consider NUnit to be user code for some reason.

Visual Studio中实际上告诉你这个以纯文本格式,如果你知道去哪里找:

Visual Studio actually tells you this in plain text, if you know where to look:

也有这一事实的堆栈跟踪证据:

There is also evidence of this fact in the stack trace:

解决方法1 :把NUnit的调试版本与调试符号。这将获得Visual Studio中把NUnit的为用户code < /一>,从而阻止用户code未处理的治疗你的异常作为。这是不平凡的,但可能会更好地工作,从长远来看。

Solution 1: Use a debug build of NUnit with debugging symbols. That will get Visual Studio to regard NUnit as user code, and thus stop treating your exceptions as "unhandled by user code". This isn’t trivial, but might work better in the long term.

解决方案2 :关闭在Visual Studio中的调试设置中的启用仅我的code复选框:

Solution 2: Turn off the "Enable Just My Code" checkbox in Visual Studio’s debugging settings:

P.S。我不考虑变通办法,让你避免使用 Assert.Throws&LT; T&GT; 干脆,但也有这样做的过程中方式

P.S. I’m not considering work-arounds whereby you avoid the use of Assert.Throws<T> altogether, but there are of course ways of doing that.

 
精彩推荐
图片推荐