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

为何非UTF-8字符串在Xunit Fact测试通过,Theory测试却失败?

为什么Xunit Theory的MemberData会改变无效UTF-16字符串?

问题根源

Xunit在处理MemberData时,会对数据进行序列化和反序列化操作(默认使用JSON序列化机制)。你构造的字符串是无效的UTF-16序列——单独的高代理项字符("😀"[0])加上'X',这种不符合UTF-16规范的序列,在序列化过程中会被序列化库自动替换成替换字符(�),导致传到测试方法里的字符串和你原本构造的不一样。

而Fact测试中,字符串是在测试方法内部直接构造的,全程在内存中生成,没有经过序列化步骤,所以能保留原始的无效UTF-16序列,自然能触发校验错误。

解决方案

要避免序列化对无效字符串的修改,你可以换一种方式传递数据,比如传递构造无效字符串所需的原始字符/数值,在测试方法内部再组装成目标字符串:

方案1:传递字符组件,在测试中组装

public static TheoryData<char, char> BadData => new TheoryData<char, char>
{
    { "😀"[0], 'X' } // 传递高代理项字符和后续字符
};

[Theory]
[MemberData(nameof(BadData))]
public void GivenAnInvalidFirstName_Validate_ShouldHaveAValidationError(char highSurrogate, char followChar)
{
    // 在测试内部构造无效字符串,跳过序列化环节
    var notUtf8 = new string(new[] { highSurrogate, followChar });
    _validator.ShouldHaveValidationErrorFor(x => x.FirstName, notUtf8);
}

方案2:传递字节数组,在测试中转成字符串

如果你的校验是针对UTF-8编码的有效性,也可以直接传递原始字节数组,在测试里转成字符串:

public static TheoryData<byte[]> BadData => new TheoryData<byte[]>
{
    { new byte[] { 0xF0, 0x9F, 0x98, (byte)'X' } } // 构造无效的UTF-8字节序列
};

[Theory]
[MemberData(nameof(BadData))]
public void GivenAnInvalidFirstName_Validate_ShouldHaveAValidationError(byte[] badBytes)
{
    // 用UTF-8解码字节数组,保留无效序列
    var notUtf8 = Encoding.UTF8.GetString(badBytes);
    _validator.ShouldHaveValidationErrorFor(x => x.FirstName, notUtf8);
}

补充说明

C#的string本质是UTF-16编码的字符序列,单独的高代理项属于非法的UTF-16序列。虽然CLR允许构造这样的字符串,但大多数序列化库(包括Xunit使用的)在处理时会自动修复这种非法序列,这就是MemberData传递后字符串变化的核心原因。

内容的提问来源于stack exchange,提问作者Pure.Krome

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 23:45:54