You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

泛型异常测试失败而非泛型测试正常的原因是什么?

异常类泛型测试断言失败问题排查

问题场景

我有一批结构一致的异常类,示例如下:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 14:25:25