C# 努尼特:为什么';t断言抛出<;T>;抓住我的例外?

C# 努尼特:为什么';t断言抛出<;T>;抓住我的例外?,c#,.net,exception,nunit,C#,.net,Exception,Nunit,尊敬的约翰·斯基特先生建议我设计一个简单的测试程序,分离并证明我遇到的问题,并重新提出这个问题。我是应尊敬的约翰·斯基特先生的要求重新发布这个问题的。这个问题源于,所以如果听起来很熟悉,请原谅我。你可以从这个问题中收集更多关于这个问题的细节 我遇到的问题涉及NUnit 2.5.9中的Assert.Throws。有时,它将无法捕获TestDelegate调用的方法中抛出的任何异常。我在下面的代码中以可复制的方式固定了这种行为。(当然,这可能是我的机器出现故障的情况™. 为了重现错误,我创建了一个包

尊敬的约翰·斯基特先生建议我设计一个简单的测试程序,分离并证明我遇到的问题,并重新提出这个问题。我是应尊敬的约翰·斯基特先生的要求重新发布这个问题的。这个问题源于,所以如果听起来很熟悉,请原谅我。你可以从这个问题中收集更多关于这个问题的细节

我遇到的问题涉及NUnit 2.5.9中的
Assert.Throws
。有时,它将无法捕获TestDelegate调用的方法中抛出的任何异常。我在下面的代码中以可复制的方式固定了这种行为。(当然,这可能是我的机器出现故障的情况™.

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

  • 第一个包含一个类,带有一个公共方法。该方法是一个扩展方法,它封装了创建
    SqlCommand
    、填充其参数并在其上调用
    ExecuteScalar
    所需的逻辑。此项目不包括其他引用
  • 第二个包含一个类和两个方法,用于测试第一个DLL中的方法是否按预期工作。此项目引用第一个DLL,并包含对NUnit框架的引用。未引用其他程序集
在调试程序中逐步完成测试时,我观察到以下情况:

  • Assert.Throws
    正确调用
    ExecuteScalar
    扩展方法
  • 正如预期的那样,参数值为null
  • ExecuteScalar
    测试其参数的空值
  • 调试器确实命中并执行包含
    抛出新ArgumentNullException(…)
    的行
  • 执行
    throw
    后,应用程序的控制不会立即转移到
    Assert.Throws
    。相反,它会在
    ExecuteScalar
    中的下一行继续
  • 一旦执行下一行代码,调试器就会中断,并显示错误“用户代码未处理参数null异常”
  • 下面给出了隔离此行为的源代码

    扩展方法

    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\u异常
    {
    使用制度;
    使用系统数据;
    使用System.Data.SqlClient;
    公共静态类1
    {
    公共静态T ExecuteScalar(此SqlConnection连接,字符串sql)
    {
    if(连接==null)
    {
    抛出新的ArgumentNullException(“连接”);
    }
    if(sql==null)
    {
    抛出新的ArgumentNullException(“sql”);
    }
    使用(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));
                }
            }
        }
    }
    
    名称空间NUnit\u测试
    {
    使用制度;
    使用System.Data.SqlClient;
    使用系统诊断;
    使用NUnit.Framework;
    利用NUnit_异常;
    [测试夹具]
    公共类NunitomalyTest
    {
    [测试]
    public void ExecuteDataSetThrowsForNullConnection()执行
    {
    抛出(()=>((SqlConnection)null.ExecuteScalar(null));
    }
    [测试]
    public void ExecuteDataSetThrowsForNullSql()
    {
    const string server=“MY-LOCAL-SQL-server”;
    const string instance=“staging”;
    string connectionString=string.Format(“数据源={0};初始目录={1};集成安全性=True;”,
    服务器,
    实例);
    使用(var连接=新的SqlConnection(connectionString))
    {
    Assert.Throws(()=>connection.ExecuteScalar(null));
    }
    }
    }
    }
    
    最终的结果是测试在不应该的情况下失败。据我所知,
    Assert.Throws
    应该捕获我的异常,测试应该通过

    更新

    我接受了Hans的建议并检查了异常对话框。我没有破坏抛出的异常,而是破坏了未处理的用户异常。显然,这就是为什么在抛出异常时调试器会闯入IDE。清除复选框修复了问题,并且
    Assert.Throws
    拾取了它。但是,如果我没有这样做,则s、 我不能只按F5继续执行,否则异常将变成
    NullReferenceException


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

    实际发生的情况是
    断言。抛出
    是否捕获您的异常,但是Visual Studio会在第一次出现异常时停止。您可以通过唱F5;Visual Studio将愉快地继续执行

    作为例外助手告诉您,异常是未处理的<强> >用户代码< /强>。因此我们知道VisualStudio出于某种原因不认为NUnbe是用户代码。

    如果您知道在哪里查找,Visual Studio实际上会以纯文本形式告诉您:

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

    解决方案1:使用带有调试符号的NUnit调试版本。这样,就不会将异常视为“未经用户代码处理”。这不是小事,但从长远来看可能会更好

    解决方案2:关闭Visual Studio调试设置中的“仅启用我的代码”复选框:

    另外,我没有考虑避免使用
    Assert.Thr的变通方法