Assert.AreEqual 和 Assert.AreSame 的区别在哪
两者都用于判断两个值是否相等,但语义和行为完全不同:
AreEqual比较的是「值相等」(调用
Equals()或重载的 ==),
AreSame比较的是「引用相等」(即是否指向同一内存地址)。 对值类型(如
int、
DateTime),
AreEqual和
AreSame行为一致,因为值类型没有引用共享问题 对引用类型(如
string、自定义类),
AreEqual("a", "a") 通常通过,但 AreSame(new string('a', 1), new string('a', 1)) 一定失败
string是特例:由于字符串驻留(interning),
AreSame("hello", "hello") 可能意外通过,但不应依赖此行为
Assert.AreEqual(5, 2 + 3); // ✅ 通过
Assert.AreSame(new List<int>(), new List<int>()); // ❌ 失败:两个不同实例
Assert.AreEqual(new List<int> {1}, new List<int> {1}); // ❌ 失败:List<T> 默认不重写 Equals
Assert.ThrowsException 怎么捕获具体异常并验证消息
它不只是检查是否抛出异常,还能返回异常对象,方便进一步断言其属性,比如
Message或自定义字段。 必须用泛型指定期望的异常类型,否则会匹配所有派生类型(如
Assert.ThrowsException<exception></exception>过于宽泛) 不能直接在 lambda 中写
throw new ArgumentException(...),必须是被测代码实际执行路径中抛出的 若被测方法是异步的,要用
Assert.ThrowsExceptionAsync<t></t>
var ex = Assert.ThrowsException<ArgumentException>(() => ParseAge("-5"));
Assert.IsTrue(ex.Message.Contains("age")); // 验证消息内容
Assert.AreEqual("age", ex.ParamName); // 验证 ParamName 字段
Assert.That 在 NUnit 中为什么比 MSTest 的 Assert 更灵活
Assert.That是 NUnit 的核心断言入口,基于约束(Constraint)模型,天然支持链式表达和组合条件,而 MSTest 的
Assert是静态方法集合,扩展性弱。
Assert.That(actual, Is.EqualTo(expected).Within(0.001))可轻松加容差,MSTest 需用
AreEqual(double, double, double)且仅限 double
Assert.That(list, Has.Count.EqualTo(3).And.All.GreaterThan(0))一行完成多个维度校验 NUnit 支持自定义
Constraint,MSTest 几乎无法扩展断言逻辑 MSTest v3 已引入部分类似语法(如
Assert.That别名),但底层仍不支持约束组合
测试中 Assert.Fail() 和 try/catch + Assert 一起用会出什么问题
直接在
try块里调用
Assert.Fail()并不能达到“预期异常未抛出就失败”的目的——它会让测试无条件失败,掩盖了真实逻辑分支。 正确做法是把被测代码放在
try外,用
Assert.ThrowsException托管整个执行和异常捕获过程 手动
try/catch后再
Assert.Fail()属于反模式:既冗余又易漏掉
catch之外的异常 如果真要手写控制流(极少见),必须确保
catch块外有
Assert.Fail(),否则异常未发生时测试会静默通过
// ❌ 错误:无论是否抛异常,Assert.Fail() 都执行
try { DoSomething(); } catch (InvalidOperationException) { }
Assert.Fail("Expected exception not thrown");
// ✅ 正确:交给框架处理
Assert.ThrowsException<InvalidOperationException>(() => DoSomething());
断言不是越写越多就越安全;关键在于选对方法、理解语义差异,尤其是引用/值比较、异常捕获时机、以及测试框架底层机制的差异。这些地方一错,测试本身就成了漏洞。 