我在尊敬的John Skeet先生的要求下重新发布了这个问题,他建议我设计一个简单的测试程序,隔离并演示我遇到的问题并重新发布问题。这个问题源于this one,所以如果听起来非常熟悉,请原谅我。您可以从中收集有关此问题的额外详细信息。
我遇到的问题是来自NUnit 2.5.9的Assert.Throws<T>
。有时,它无法捕获由TestDelegate调用的方法中抛出的任何异常。我已经在下面的代码中以可重现的方式确定了这种行为。 (尽管如此,这可能是Fails On My Machine™的案例。
为了重现错误,我创建了一个包含两个C#DLL项目的解决方案:
SqlCommand
所需的逻辑,填充其参数并在其上调用ExecuteScalar
。该项目不包括其他参考文献。当我在调试器中逐步完成测试时,我会观察到以下内容:
Assert.Throws
正确调用ExecuteScalar<T>
扩展方法。 ExecuteScalar<T>
测试其参数的空值。throw new ArgumentNullException(...)
。throw
后,对应用程序的控制不会立即转移到Assert.Throws
。相反,它继续在ExecuteScalar<T>
。下面给出了隔离此行为的源代码。
扩展方法
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<T>
应该捕获我的异常并且测试应该通过。
更新
我接受了汉斯的建议并检查了“例外”对话框。我没有打破抛出的异常,但我打破了未处理的用户异常。显然,这就是抛出异常时调试器进入IDE的原因。清除复选框可修复问题,Assert.Throws<T>
将其选中。但是,如果我没有这样做,我不能只按F5继续执行,或者异常将成为NullReferenceException
。
现在问题是:我可以基于每个项目配置异常中断吗?我只是想在测试时这样做,但不是一般。
答案 0 :(得分:15)
实际发生的是,Assert.Throws
会捕获您的异常,但无论如何,Visual Studio会停止第一次机会异常。您只需按F5即可查看; Visual Studio很乐意继续执行。
正如异常帮助程序告诉您的那样,异常未由用户代码处理。因此我们知道Visual Studio因某种原因不认为NUnit是用户代码。
Visual Studio实际上以纯文本形式告诉您,如果您知道在哪里查看:
堆栈跟踪中也有证据表明这一事实:
解决方案1 :使用NUnit的调试版本和调试符号。那个will get Visual Studio to regard NUnit as user code,因此停止将您的异常视为“未被用户代码处理”。这不是微不足道的,但从长远来看可能会更好。
解决方案2 :在Visual Studio的调试设置中关闭“启用我的代码”复选框:
P.S。我不考虑解决方法,你完全避免使用Assert.Throws<T>
,但当然有办法实现这一点。