简体   繁体   English

C#单元测试Newtonsoft JSON模型

[英]C# Unit testing a Newtonsoft JSON model

I am starting with unit testing and am doing so with XUnit. 我从单元测试开始,而与XUnit一起这样做。

I have a model : 我有一个模特:

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; }
    }
}

and I have a JSON from which I am getting values like : 而且我有一个JSON,我从中获取值:

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

So I want to test that model and for so I though of: 因此,我想测试该模型,为此,尽管如此:

  • As CivicNumber is Required I want to make sure there is no Null value or empty string in the JSON, so a test with a null value crashing the test 由于需要CivicNumber,因此我想确保JSON中没有Null值或空字符串,因此具有null值的测试会使测试崩溃
  • If ever someone would change the JSON and remove one of the field in the properties object, like city_name, then the test would fail so I know I either need to adapt my model or get back to the original JSON format 如果有人更改了JSON并删除了属性对象中的一个字段(例如city_name),则测试将失败,所以我知道我需要调整模型或返回原始JSON格式
  • Check that format of JSON values, in this case all of them are string, are ok to be injected in my model 检查JSON值的格式,在这种情况下,它们都是字符串,可以插入模型中
  • Run an injection from a shorter version of a JSON file with like only 3 entries into the model to make sure everything is ok 从较短版本的JSON文件运行注入,仅向模型中输入3个条目,以确保一切正常

Am I on the right path and how to do this, that would greatly help and kickstart me for a lot of other things. 我在正确的道路上以及如何做到这一点,这将极大地帮助我启动其他许多事情。

THanks! 谢谢!

This wouldn't be something you need to unit test, but rather utilize Newtonsoft's JSON validation. 不需要进行单元测试,而是利用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);

From there you can iterate through the validation errors if you want to elaborate for a response to the caller. 如果要详细说明对调用者的响应,可以从那里遍历验证错误。

Unit tests are suited to asserting static code behaviour so if your JSON schema specifies rules, the assertion that a unit test does is: 单元测试适合于声明静态代码行为,因此,如果您的JSON模式指定规则,则单元测试执行的声明为:

  • Assert that your code validated the JSON Schema and rejected it if invalid. 断言您的代码验证了JSON模式,如果无效,则拒绝它。
  • Any behaviour your code should do with valid JSON data scenarios. 您的代码应使用有效的JSON数据方案执行任何行为。

** Edit ** To elaborate on using the validation, then unit testing that the validation is performed: **编辑**要详细说明如何使用验证,然后对执行验证的单元进行测试:

Lets say we have an API that accepts a JSON data as a string rather than as a class: (where we should be able to use model.IsValid()) 可以说我们有一个API,它接受JSON数据作为字符串而不是类:(我们应该能够使用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
    };
  }
}

The unit test would be against the Controller, to assert that it used the validator to ensure the JSON data was valid: 单元测试将针对Controller,以断言它使用了验证器来确保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);
}

What this test accomplishes is asserting that our controller will call the validation logic to assess our provided JSON. 该测试完成的工作是断言我们的控制器将调用验证逻辑来​​评估我们提供的JSON。 If someone modifies the controller and removes the call to Validate, the test will fail. 如果有人修改控制器并删除对Validate的调用,则测试将失败。

You don't need to compose a JSON object for a test like this, "fred" will be fine as it's just a placeholder for the mock to recognize. 您不需要为这样的测试编写JSON对象,“ fred”就可以了,因为它只是模拟可以识别的占位符。 The mock in this scenario is configured that: "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." 在这种情况下,模拟程序配置为:“我希望使用特定的值来调用。[fred]”“获得该值时,我会说它无效,并且将包含一组特定的验证错误。” From there you can assert the result response from the Controller to see if it reflects the validation errors. 从那里,您可以断言Controller的结果响应,以查看它是否反映了验证错误。 We also ask the Mock at the end to verify that it was called with the specific value. 我们还要求最后的Mock验证是否使用特定值调用了它。

Now if you also want to enforce/assert against changes to your schema then you can write tests against your validator itself. 现在,如果您还想强制执行/声明对架构的更改,则可以针对验证器本身编写测试。 Such as to detect someone removing or changing an attribute on your model. 例如检测有人删除或更改模型上的属性。

[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....
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM