C# 单元测试 Newtonsoft JSON 模型

C# Unit testing a Newtonsoft JSON model

我开始进行单元测试,并且正在使用 XUnit 进行测试。

我有一个模型:

using Newtonsoft.Json;
namespace HomeAddressSearch
{
    public class Properties
    {
        [Required]
        [JsonProperty(PropertyName = "civic_number")]
        public string CivicNumber { get; set; }

        [JsonProperty(PropertyName = "address")]
        public string Address { get; set; }

        [JsonProperty(PropertyName = "postal_code")]
        public string PostalCode { get; set; }

        [JsonProperty(PropertyName = "city_name")]
        public string CityName { get; set; }
    }
}

我有一个 JSON,我从中得到的值如下:

[
  {
    "properties": {
      "civic_number": "100",
      "address": "100, king street",
      "postal_code": "H0H0H0",
      "city_name": "Windsor"
    },
    "owner": {
      "name": "John Doe",
      "age": "40"
    },
  }
]

所以我想测试那个模型,为此我想到了:

我走在正确的道路上吗?如何去做,这将极大地帮助和启动我做很多其他事情。

谢谢!

这不是您需要进行单元测试的东西,而是利用 Newtonsoft 的 JSON 验证。

// Generate a JSON schema from your object.
var schemaGenerator = new JSchemaGenerator();
var schema = schemaGenerator.Generate(typeof(HomeAddressSearch.Properties)); 

// Parse the JSON passed in, then validate it against the schema before proceeding.
List<ValidationErrors> validationErrors;
JObject addressToValidate = JObject.Parse(jsonStringToValidate);
bool isValid = addressToValidate.IsValid(schema, out validationErrors);

如果您想详细说明对调用者的响应,您可以从那里遍历验证错误。

单元测试适合断言静态代码行为,因此如果您的 JSON 模式指定规则,则单元测试所做的断言是:

  • 断言您的代码验证了 JSON 模式,如果无效则拒绝它。
  • 您的代码应对有效 JSON 数据场景执行的任何行为。

** 编辑 ** 详细说明使用验证,然后执行验证的单元测试:

假设我们有一个 API 接受 JSON 数据作为字符串而不是 class:(我们应该能够使用 model.IsValid ())

public class AddressController : ApiController
{
  private readonly IJsonEntityValidator<HomeAddressSearch.Properties> _validator = null;

  public AddressController(IJsonEntityValidator validator)
  {
    _validator = validator;
  }
  public ActionResult Search(string jsonModel)
  { 
    if(string.IsNullOrEmpty(jsonModel))
      // handle response for missing model.

    var result = _validator.Validate<HomeAddressSearch.Properties>(jsonModel);
    if (!result.IsValid) 
      // handle validation failure for the model.
  }
}

public class JSonEntityValidator<T> : IJsonEntityValidator<T> where T: class
{
  IJsonEntityValidator<T>.Validate(string jsonModel)
  {
    // Generate a JSON schema from your object.
    var schemaGenerator = new JSchemaGenerator();
    var schema = schemaGenerator.Generate(typeof(T)); 

    // Parse the JSON passed in, then validate it against the schema before proceeding.
    List<ValidationErrors> validationErrors;
    JObject model = JObject.Parse(jsonModel);
    bool isValid = model.IsValid(schema, out validationErrors);
    return new JsonValidateResult
    {
      IsValid = isValid,
      ValidationErrors = validationErrors
    };
  }
}

单元测试将针对控制器,断言它使用验证器来确保 JSON 数据有效:

[Test]
public void EnsureHomeAddressSearchValidatesProvidedJson_InvalidJSONScenario()
{
  string testJson = buildInvalidJson(); // the value here doesn't matter beyond being recognized by our mock as being passed to the validation.
  var mockValidator = new Mock<IJsonEntityValidator<HomeAddressSearch.Properties>>();
  mockValidator.Setup(x => x.Validate(testJson)
    .Returns(new JsonValidationResult { IsValid = false, ValidationErrors = new ValidationError[] { /* populate a set of validation errors. */ }.ToList()});
  var testController = new AddressController(mockValidator.Object);
  var result = testController.Search(testJson);
  // Can assess the result from the controller based on the scenario validation errors returned....

  // Validate that our controller called the validator.
  mockValidator.Verify(x => x.Validate(testJson), Times.Once);
}

这个测试完成的是断言我们的控制器将调用验证逻辑来​​评估我们提供的 JSON。如果有人修改控制器并删除对 Validate 的调用,测试将失败。

您不需要为这样的测试编写 JSON 对象,"fred" 就可以了,因为它只是模拟识别的占位符。此场景中的模拟配置为: "I expect to be called with a specific value. [fred]" "When I get that value, I'm going to say it isn't valid, and I'm going to include a specific set of validation errors." 从那里您可以断言控制器的结果响应以查看它是否反映了验证错误。我们还要求最后的 Mock 验证它是用特定值调用的。

现在,如果您还想 enforce/assert 反对对架构的更改,那么您可以针对验证器本身编写测试。例如检测有人删除或更改模型上的属性。

[Test]
EnsureHomeAddressValidatorExpectsCivicNumberIsRequired()
{
  var testJson = generateJsonMissingCivicNumber();
  IJsonEntityValidator<HomeAddressSearch.Properties> testValidator = new JsonEntityValidator<HomeAddressSearch.Properties>();
  var result = testValidator.Validate(testJson);
  Assert.IsFalse(result.IsValid);
  // Assert result.ValidationErrors....
}