如何对Azure函数ObjectResult的JSON序列化进行单元测试
问题解决:Azure函数.NET6进程内/隔离模式序列化差异及单元测试方案
一、序列化差异的根源与修复
.NET6 Azure函数进程内模式默认使用Newtonsoft.Json序列化,隔离模式默认使用System.Text.Json,两者默认配置差异导致了你的问题:
@odata.type字段变形:System.Text.Json默认不兼容Newtonsoft.Json的属性命名规则,若未显式配置,带@前缀的属性名会被错误处理- 空值未被忽略:System.Text.Json默认序列化空值属性,而Newtonsoft.Json默认忽略空值
修复方案(隔离模式下)
1. 全局配置System.Text.Json匹配Newtonsoft行为
在隔离模式项目的Program.cs中,配置函数的Json序列化选项,统一对齐进程内的序列化逻辑:
var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults(builder => { builder.ConfigureJsonSerializerOptions(options => { // 序列化时忽略空值属性 options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; // 保留属性名原样输出,确保@odata.type不会被修改 options.PropertyNamingPolicy = null; }); }) .Build(); host.Run();
2. 针对单个属性显式指定序列化名称
如果不需要全局修改配置,可以在Claims类的属性上添加System.Text.Json特性,强制指定序列化后的字段名:
public class Claims { [JsonPropertyName("@odata.type")] public string? ODataType { get; set; } = "#Microsoft.Open.AzureAD.Model.CustomClaim"; public string? ClaimType { get; set; } public string? ClaimValue { get; set; } public string? AnotherValue { get; set; } }
二、单元测试:无需运行函数验证序列化结果
1. 进程内模式测试(解析IActionResult)
对于返回OkObjectResult的进程内函数,可直接提取返回对象并使用Newtonsoft.Json序列化验证:
[Test] public void ProcessInMode_SerializesCorrectly() { // 实例化函数类 var function = new CustomClaimsProviderFunction(); // 调用函数获取IActionResult var result = function.Run(new HttpRequestMessage()); // 转换为OkObjectResult并验证非空 var okResult = result as OkObjectResult; Assert.NotNull(okResult); // 使用Newtonsoft.Json序列化(匹配进程内默认行为) var json = JsonConvert.SerializeObject(okResult.Value, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, ContractResolver = new DefaultContractResolver() }); // 断言序列化结果符合预期 Assert.That(json, Does.Contain("@odata.type")); Assert.That(json, Does.Not.Contain("anotherValue: null")); }
2. 隔离模式测试(验证序列化逻辑)
隔离模式函数通常返回HttpResponseData,可直接序列化要返回的对象(使用和函数一致的配置),或模拟函数上下文测试完整逻辑:
方式一:直接序列化对象验证
[Test] public void IsolatedMode_SerializesCorrectly() { // 准备测试用的Claims对象 var claims = new Claims { ODataType = "#Microsoft.Open.AzureAD.Model.CustomClaim", ClaimType = "testType", ClaimValue = "testValue", AnotherValue = null }; // 使用和函数一致的JsonSerializerOptions var options = new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNamingPolicy = null }; // 序列化对象 var json = JsonSerializer.Serialize(claims, options); // 断言结果符合要求 Assert.That(json, Does.Contain("@odata.type")); Assert.That(json, Does.Not.Contain("anotherValue")); }
方式二:模拟函数上下文测试完整流程
借助Microsoft.Azure.Functions.Worker.Testing包模拟运行环境:
[Test] public async Task IsolatedFunction_ReturnsCorrectJson() { // 创建配置好序列化选项的测试主机 using var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() .ConfigureJsonSerializerOptions(options => { options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; options.PropertyNamingPolicy = null; }) .Build(); // 模拟HttpRequestData var request = TestHttpRequestData.Create(host.Services); // 实例化函数并调用 var function = new CustomClaimsProviderIsolatedFunction(); var response = await function.Run(request); // 读取响应内容 var responseBody = await new StreamReader(response.Body).ReadToEndAsync(); // 断言响应结果 Assert.That(responseBody, Does.Contain("@odata.type")); Assert.That(responseBody, Does.Not.Contain("anotherValue: null")); }
三、结果验证
应用修复后,隔离模式的输出将和进程内模式一致:
{ "@odata.type": "#Microsoft.Open.AzureAD.Model.CustomClaim", "claimType": "testType", "claimValue": "testValue" }
内容的提问来源于stack exchange,提问作者Richardissimo
相关产品推荐
相关产品推荐

