如何在C# Web API中为JSON Schema验证编写xUnit单元测试
xUnit单元测试JSON Schema验证逻辑指南
第一步:重构验证逻辑(解耦数据库依赖)
先把原API里的验证逻辑抽成独立方法,避免直接依赖数据库查询,方便测试时传入自定义的Schema:
using System.Collections.Generic; using System.Threading.Tasks; using Newtonsoft.Json.Linq; using JsonSchema.Net; // 按实际引用的Schema库调整 public class JsonSchemaValidator { // 抽离验证逻辑,接收输入JSON和Schema JSON字符串 public async Task<ICollection<ValidationError>> ValidateJsonAsync(string inputJson, string schemaJson) { var schema = await JsonSchema.FromJsonAsync(schemaJson); var jsonToken = JToken.Parse(inputJson); return schema.Validate(jsonToken).ToList(); } }
原API代码可改为调用该方法:
// 从数据库获取schemaJson(业务代码逻辑) var schemaJson = await _dbContext.Schemas.FirstAsync(s => s.Id == schemaId); var validator = new JsonSchemaValidator(); var errors = await validator.ValidateJsonAsync(inputfromAPI, schemaJson); if (errors.Count > 0) { return BadRequest(errors); } // 后续业务逻辑
第二步:编写xUnit测试案例
测试1:符合Schema的JSON验证通过
using Xunit; using System.Threading.Tasks; public class JsonSchemaValidatorTests { private readonly JsonSchemaValidator _validator = new JsonSchemaValidator(); [Fact] public async Task ValidateJsonAsync_ValidJsonAgainstSchema_ReturnsNoErrors() { // 模拟Schema:要求必须有"name"字符串和"age"数字字段 var mockSchema = @"{ ""type"": ""object"", ""properties"": { ""name"": { ""type"": ""string"" }, ""age"": { ""type"": ""integer"" } }, ""required"": [""name"", ""age""] }"; // 符合Schema的输入JSON var validInput = @"{ ""name"": ""Alice"", ""age"": 30 }"; var errors = await _validator.ValidateJsonAsync(validInput, mockSchema); Assert.Empty(errors); } }
测试2:不符合Schema的JSON返回错误
[Fact] public async Task ValidateJsonAsync_InvalidJsonAgainstSchema_ReturnsErrors() { var mockSchema = @"{ ""type"": ""object"", ""properties"": { ""name"": { ""type"": ""string"" }, ""age"": { ""type"": ""integer"" } }, ""required"": [""name"", ""age""] }"; // 输入缺少必填的"age"字段 var invalidInput = @"{ ""name"": ""Bob"" }"; var errors = await _validator.ValidateJsonAsync(invalidInput, mockSchema); Assert.NotEmpty(errors); Assert.Contains(errors, e => e.Message.Contains("Required properties are missing from object: age")); }
测试3:无效JSON输入抛出异常
[Fact] public async Task ValidateJsonAsync_InvalidJsonInput_ThrowsException() { var mockSchema = @"{ ""type"": ""object"" }"; // 格式错误的JSON(缺少闭合括号) var invalidJson = @"{ ""name"": ""Charlie"" "; await Assert.ThrowsAsync<Newtonsoft.Json.JsonReaderException>(() => _validator.ValidateJsonAsync(invalidJson, mockSchema)); }
测试4:无效Schema抛出异常
[Fact] public async Task ValidateJsonAsync_InvalidSchema_ThrowsException() { // 格式错误的Schema(缺少闭合括号) var invalidSchema = @"{ ""type"": ""object"" "; var validJson = @"{ ""name"": ""Dave"" }"; await Assert.ThrowsAsync<JsonSchemaException>(() => _validator.ValidateJsonAsync(validJson, invalidSchema)); }
关键说明
- 测试时直接手写模拟的Schema JSON字符串,完全脱离数据库依赖,保证单元测试的隔离性
- 用
async/await替代原代码中的.Result,避免异步操作死锁,符合异步编程规范 - 覆盖核心场景:合法输入、非法输入、无效JSON、无效Schema,确保验证逻辑的稳定性
内容的提问来源于stack exchange,提问作者learningdotnet
相关产品推荐
相关产品推荐

