泛型异常测试失败而非泛型测试正常的原因是什么?
异常类泛型测试断言失败问题排查
问题场景
我有一批结构一致的异常类,示例如下:
public class ContainerCouldNotStartedException : Exception { private static readonly string Text = "Container could not be started: {0}. Please see docker logging for more information"; public ContainerCouldNotStartedException() { } public ContainerCouldNotStartedException(string name) : base(String.Format(Text, name)) { } }
为了避免给10个异常类编写重复测试代码,我先实现了非泛型测试方法:
public static void Test_String_Is_In_Message(Type type, string name) { ConstructorInfo ctor = type.GetConstructor(new[] { typeof(string) }); dynamic exception = ctor.Invoke(new object[] { name }); Assert.That(exception.Message, Does.Contain(name)); }
调用方式如下:
[Test] [TestCase("HarryPotter")] [TestCase("HermineGranger")] [TestCase("LuciusMalfoy")] public void Message_Has_Name(string name) { ExceptionTest.Test_String_Is_In_Message(typeof(ContainerCouldNotStartedException), name); ExceptionTest.Test_String_Is_In_Message(typeof(DatabaseNameNotCorrectException), name); ExceptionTest.Test_String_Is_In_Message(typeof(DatabaseNotCorrectException), name); ExceptionTest.Test_String_Is_In_Message(typeof(DatabaseVersionNotReadableException), name); ExceptionTest.Test_String_Is_In_Message(typeof(VersionNotFoundException), name); }
此时所有测试均能正常通过。但由于存在一个参数为GUID而非字符串的异常类,我将测试方法修改为泛型版本:
public static void Test_String_Is_In_Message<T>(Type type, T name) { ConstructorInfo ctor = type.GetConstructor(new[] { typeof(T) }); dynamic exception = ctor.Invoke(new object[] { name }); Assert.That(exception.Message, Does.Contain(name)); }
修改后所有测试均失败,调试时明明能看到参数内容存在于消息中,但断言始终不通过。
问题原因
核心问题在于Does.Contain(name)的断言逻辑:
- 非泛型版本中,
name是string类型,Does.Contain会直接检查消息字符串中是否包含该字符串内容,逻辑正常。 - 泛型版本中,
name的类型是T,Does.Contain<T>会尝试在消息字符串中查找**T类型的对象实例**,而非该对象的字符串表示。即使T是string,类型推断也会让断言使用针对string对象的匹配逻辑(而非字符串内容匹配),导致匹配失败。
简单来说:消息里存的是name的字符串值,但断言在找name这个对象本身,自然匹配不上。
解决办法
将name转换为字符串后再进行断言,确保检查的是字符串内容:
public static void Test_String_Is_In_Message<T>(Type type, T name) { ConstructorInfo ctor = type.GetConstructor(new[] { typeof(T) }); dynamic exception = ctor.Invoke(new object[] { name }); // 转换为字符串后检查 Assert.That(exception.Message, Does.Contain(name.ToString())); }
如果需要处理name为null的情况,可以增加空值判断:
public static void Test_String_Is_In_Message<T>(Type type, T name) { ConstructorInfo ctor = type.GetConstructor(new[] { typeof(T) }); dynamic exception = ctor.Invoke(new object[] { name }); string nameStr = name?.ToString() ?? string.Empty; Assert.That(exception.Message, Does.Contain(nameStr)); }
这样无论T是string还是Guid,都会基于参数的字符串表示进行匹配,断言就能正常通过。
内容的提问来源于stack exchange,提问作者PassionateDeveloper
相关产品推荐
相关产品推荐

