On using Python3.6 unittest assertRaisesRegex
to validate some JSON there are differences in the output generated between validating code under the JSON oneOf
keyword and code that is not. I am testing the removal of required
properties.
The problem is that the user expects one type of error message but another is received and assertRaisesRegex
fails as it does not match the regex.
Using http://json-schema-validator.herokuapp.com to validate the JSON I see the actual error message expected has been nested deeper.
I can get around the problem by searching for the new message when testing for oneOf
properties, but want to know if there is a better way to handle this problem that is consistent with my other tests, i.e. using the same format for the messages I expect?
Below are some examples of output that test for required
JSON properties being removed. The expected message format is marked with a =>
the unexpected with **
Example 1 (herokuapp.com
): When a required
property is removed (not under oneOf
)
[ {
"level" : "error",
"schema" : {
"loadingURI" : "#",
"pointer" : "/properties/Attr_B"
},
"instance" : {
"pointer" : "/Attr_B"
},
"domain" : "validation",
"keyword" : "required",
=>"message" : "object has missing required properties ([\"alpha\"])",
"required" : [ "alpha", "beta" ],
"missing" : [ "alpha" ]
} ]
Example 2 (herokuapp.com
): When a oneOf
nested required
property is removed
[ {
"level" : "error",
"schema" : {
"loadingURI" : "#",
"pointer" : "/properties/Attr_B/properties/beta"
},
"instance" : {
"pointer" : "/Attr_B/beta"
},
"domain" : "validation",
"keyword" : "oneOf",
**"message" : "instance failed to match exactly one schema (matched 0 out of 1)",
"matched" : 0,
"nrSchemas" : 1,
"reports" : {
"/properties/Attr_B/properties/beta/oneOf/0" : [ {
"level" : "error",
"schema" : {
"loadingURI" : "#",
"pointer" : "/properties/Attr_B/properties/beta/oneOf/0"
},
"instance" : {
"pointer" : "/Attr_B/beta"
},
"domain" : "validation",
"keyword" : "required",
=> "message" : "object has missing required properties ([\"min_percentage\"])",
"required" : [ "max_percentage", "min_percentage" ],
"missing" : [ "min_percentage" ]
} ]
}
} ]
Within my unittests using jsonschema
, the messages differ but the problem is the same.
Example 1 (jsonschema
): When a required
property is removed (not under oneOf
)
E
======================================================================
ERROR: test_valid__JSON_against_schema (__main__.SchemaTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 42, in test_valid__JSON_against_schema
validate(test_json, test_schema)
File "/local_scratch/tools/PACKAGES/Python-3.6.1/lib/python3.6/site-packages/jsonschema/validators.py", line 541, in validate
cls(schema, *args, **kwargs).validate(instance)
File "/local_scratch/tools/PACKAGES/Python-3.6.1/lib/python3.6/site-packages/jsonschema/validators.py", line 130, in validate
raise error
=> jsonschema.exceptions.ValidationError: 'alpha' is a required property
Failed validating 'required' in schema['properties']['Attr_B']:
{'additionalProperties': False,
'properties': {'alpha': {'enum': ['a', 'b'], 'type': 'string'},
'beta': {'oneOf': [{'additionalProperties': False,
'properties': {'max_percentage': {'additionalProperties': False,
'maximum': 150,
'minimum': 10,
'type': 'integer'},
'min_percentage': {'additionalProperties': False,
'maximum': 50,
'minimum': 1,
'type': 'integer'}},
'required': ['min_percentage',
'max_percentage'],
'type': 'object'}]}},
'required': ['alpha', 'beta'],
'type': 'object'}
On instance['Attr_B']:
{'beta': {'max_percentage': 24, 'min_percentage': 24}}
----------------------------------------------------------------------
Ran 1 test in 0.004s
FAILED (errors=1)
Example 2 (jsonschema
): When a oneOf
nested required
property is removed
E
======================================================================
ERROR: test_valid__JSON_against_schema (__main__.SchemaTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 42, in test_valid__JSON_against_schema
validate(test_json, test_schema)
File "/local_scratch/tools/PACKAGES/Python-3.6.1/lib/python3.6/site-packages/jsonschema/validators.py", line 541, in validate
cls(schema, *args, **kwargs).validate(instance)
File "/local_scratch/tools/PACKAGES/Python-3.6.1/lib/python3.6/site-packages/jsonschema/validators.py", line 130, in validate
raise error
** jsonschema.exceptions.ValidationError: {'max_percentage': 24} is not valid under any of the given schemas
Failed validating 'oneOf' in schema['properties']['Attr_B']['properties']['beta']:
{'oneOf': [{'additionalProperties': False,
'properties': {'max_percentage': {'additionalProperties': False,
'maximum': 150,
'minimum': 10,
'type': 'integer'},
'min_percentage': {'additionalProperties': False,
'maximum': 50,
'minimum': 1,
'type': 'integer'}},
'required': ['min_percentage', 'max_percentage'],
'type': 'object'}]}
On instance['Attr_B']['beta']:
{'max_percentage': 24}
----------------------------------------------------------------------
Ran 1 test in 0.005s
FAILED (errors=1)
I am validating against this schema
{
"id": "a-schema",
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "my schema",
"type": "object",
"properties": {
"Attr_A": {
"type": "integer",
"minimum" : 1,
"maximum" : 200,
"additionalProperties": false
},
"Attr_B": {
"type": "object",
"properties": {
"alpha": {
"type": "string",
"enum": ["a", "b"]
},
"beta": {
"oneOf": [
{
"type": "object",
"properties": {
"min_percentage": {
"type": "integer",
"minimum" : 1,
"maximum" : 50,
"additionalProperties": false
},
"max_percentage": {
"type": "integer",
"minimum" : 10,
"maximum" : 150,
"additionalProperties": false
}
},
"additionalProperties": false,
"required": ["min_percentage", "max_percentage"]
}
]
}
},
"additionalProperties": false,
"required": ["alpha", "beta"]
},
"Attr_C": {
"type": "object",
"properties": {
"min_percentage": {
"type": "integer",
"minimum" : 1,
"maximum" : 50,
"additionalProperties": false
},
"max_percentage": {
"type": "integer",
"minimum": 10,
"maximum": 150,
"additionalProperties": false
}
},
"additionalProperties": false,
"required": ["min_percentage", "max_percentage"]
}
},
"additionalProperties": false,
"required": ["Attr_A", "Attr_B", "Attr_C"]
}
Here is my instance data
{
"Attr_A": 123,
"Attr_B": {
"alpha": "a",
"beta": {
"min_percentage": 20,
"max_percentage": 24
}
},
"Attr_C": {
"min_percentage": 20,
"max_percentage": 24
}
}
These are my tests
with open("schema.json") as schema_file:
test_schema = json.load(schema_file)
schema_file.close()
with open("test.json") as json_file:
test_json = json.load(json_file)
json_file.close()
### Test the whole JSON is valid against the Schema
def test_valid__JSON_against_schema(self):
global test_schema
global test_json
validate(test_json, test_schema)
### 'Attr_B.alpha' test
def test_missing__Attr_B_alpha(self):
global test_schema
global test_json
dict_copy = copy.deepcopy(test_json)
dict_copy.pop("alpha")
msg = "'alpha' is a required property"
with self.assertRaisesRegex(ValidationError, msg):
validate(dict_copy, test_schema)
### 'Attr_B.beta.min_percentage' test
def test_missing__Attr_B_beta_min_percentage(self):
global test_schema
global test_json
dict_copy = copy.deepcopy(test_json)
test_info = dict_copy["Attr_B"]
beta= test_info["beta"]
beta.pop("min_percentage")
msg = "'min_percentage' is a required property"
with self.assertRaisesRegex(ValidationError, msg):
validate(dict_copy, test_schema)