为何非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
相关产品推荐
相关产品推荐

