Skip to content

Commit bc5d5eb

Browse files
committed
Added seeResponseIsValidOnJsonSchema
Resolves Codeception/Codeception#2472
1 parent a34a92b commit bc5d5eb

File tree

9 files changed

+180
-1
lines changed

9 files changed

+180
-1
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"require": {
1616
"php": ">=5.6.0 <8.0",
1717
"flow/jsonpath": "^0.5",
18-
"codeception/codeception": "^4.0"
18+
"codeception/codeception": "^4.0",
19+
"justinrainbow/json-schema": "^5.2.9"
1920
},
2021
"require-dev": {
2122
"codeception/util-robohelpers": "dev-master",

src/Codeception/Module/REST.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
use Codeception\Util\JsonType;
2020
use Codeception\Util\XmlStructure;
2121
use Codeception\Util\Soap as XmlUtils;
22+
use JsonSchema\Validator as JsonSchemaValidator;
23+
use JsonSchema\Constraints\Constraint as JsonContraint;
2224

2325
/**
2426
* Module for testing REST WebService.
@@ -841,6 +843,57 @@ public function seeResponseContainsJson($json = [])
841843
);
842844
}
843845

846+
/**
847+
* Checks whether last response matches the supplied json schema
848+
* Supply schema as json string
849+
*
850+
* @part json
851+
*/
852+
public function seeResponseIsValidOnJsonSchema($schema)
853+
{
854+
$responseContent = $this->connectionModule->_getResponseContent();
855+
\PHPUnit\Framework\Assert::assertNotEquals('', $responseContent, 'response is empty');
856+
$responseObject = json_decode($responseContent);
857+
$errorCode = json_last_error();
858+
$errorMessage = json_last_error_msg();
859+
\PHPUnit\Framework\Assert::assertEquals(
860+
JSON_ERROR_NONE,
861+
$errorCode,
862+
sprintf(
863+
"Invalid json: %s. System message: %s.",
864+
$responseContent,
865+
$errorMessage
866+
)
867+
);
868+
869+
\PHPUnit\Framework\Assert::assertNotEquals('', $schema, 'schema is empty');
870+
$schemaObject = json_decode($schema, true);
871+
$errorCode = json_last_error();
872+
$errorMessage = json_last_error_msg();
873+
\PHPUnit\Framework\Assert::assertEquals(
874+
JSON_ERROR_NONE,
875+
$errorCode,
876+
sprintf(
877+
"Invalid schema json: %s. System message: %s.",
878+
$responseContent,
879+
$errorMessage
880+
)
881+
);
882+
883+
$validator = new JsonSchemaValidator();
884+
$validator->validate($responseObject, $schemaObject, JsonContraint::CHECK_MODE_VALIDATE_SCHEMA);
885+
$outcome = $validator->isValid();
886+
$error = "";
887+
if (!$outcome) {
888+
$errors = $validator->getErrors();
889+
$error = array_shift($errors)["message"];
890+
}
891+
\PHPUnit\Framework\Assert::assertTrue(
892+
$outcome,
893+
$error
894+
);
895+
}
896+
844897
/**
845898
* Returns current response so that it can be used in next scenario steps.
846899
*
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"firstName": "John",
3+
"lastName": "Doe",
4+
"age": -10
5+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"fruits": [ "apple", "orange", "pear" ],
3+
"vegetables": [
4+
{
5+
"veggieName": "potato",
6+
"veggieLike": true
7+
},
8+
{
9+
"veggieName": "broccoli",
10+
"veggieLike": "false"
11+
}
12+
]
13+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"firstName": "John",
3+
"lastName": "Doe",
4+
"age": 21
5+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"fruits": [ "apple", "orange", "pear" ],
3+
"vegetables": [
4+
{
5+
"veggieName": "potato",
6+
"veggieLike": true
7+
},
8+
{
9+
"veggieName": "broccoli",
10+
"veggieLike": false
11+
}
12+
]
13+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$id": "https://example.com/person.schema.json",
3+
"$schema": "http://json-schema.org/draft-07/schema#",
4+
"title": "Person",
5+
"type": "object",
6+
"properties": {
7+
"firstName": {
8+
"type": "string",
9+
"description": "The person's first name."
10+
},
11+
"lastName": {
12+
"type": "string",
13+
"description": "The person's last name."
14+
},
15+
"age": {
16+
"description": "Age in years which must be equal to or greater than zero.",
17+
"type": "integer",
18+
"minimum": 0
19+
}
20+
}
21+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"$id": "https://example.com/arrays.schema.json",
3+
"$schema": "http://json-schema.org/draft-07/schema#",
4+
"description": "A representation of a person, company, organization, or place",
5+
"type": "object",
6+
"properties": {
7+
"fruits": {
8+
"type": "array",
9+
"items": {
10+
"type": "string"
11+
}
12+
},
13+
"vegetables": {
14+
"type": "array",
15+
"items": { "$ref": "#/definitions/veggie" }
16+
}
17+
},
18+
"definitions": {
19+
"veggie": {
20+
"type": "object",
21+
"required": [ "veggieName", "veggieLike" ],
22+
"properties": {
23+
"veggieName": {
24+
"type": "string",
25+
"description": "The name of the vegetable."
26+
},
27+
"veggieLike": {
28+
"type": "boolean",
29+
"description": "Do I like this vegetable?"
30+
}
31+
}
32+
}
33+
}
34+
}

tests/unit/Codeception/Module/RestTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,29 @@ public function testAmDigestAuthenticatedThrowsExceptionWithFunctionalModules()
442442
$this->module->amDigestAuthenticated('username', 'password');
443443
}
444444

445+
/**
446+
* @param $schema
447+
* @param $response
448+
* @param $outcome
449+
* @param $error
450+
*
451+
* @dataProvider schemaAndResponse
452+
*/
453+
454+
public function testSeeResponseIsValidOnJsonSchemachesJsonSchema($schema, $response, $outcome, $error) {
455+
456+
$response = file_get_contents(codecept_data_dir($response));
457+
$this->setStubResponse($response);
458+
459+
$validSchema = file_get_contents(codecept_data_dir($schema));
460+
461+
if (!$outcome) {
462+
$this->expectExceptionMessage($error);
463+
$this->shouldFail();
464+
}
465+
$this->module->seeResponseIsValidOnJsonSchema($validSchema);
466+
}
467+
445468
/**
446469
* @param $configUrl
447470
* @param $requestUrl
@@ -481,6 +504,17 @@ public function testRestExecute($configUrl, $requestUrl, $expectedFullUrl)
481504
$module->sendGET($requestUrl);
482505
}
483506

507+
public static function schemaAndResponse()
508+
{
509+
return [
510+
//schema, responsefile, valid
511+
['schemas/basic-schema.json', 'responses/valid-basic-schema.json', true, ""],
512+
['schemas/basic-schema.json', 'responses/invalid-basic-schema.json', false, "Must have a minimum value of 0"],
513+
['schemas/complex-schema.json', 'responses/valid-complex-schema.json', true, ""],
514+
['schemas/complex-schema.json', 'responses/invalid-complex-schema.json', false, "String value found, but a boolean is required"]
515+
];
516+
}
517+
484518
public static function configAndRequestUrls()
485519
{
486520
return [

0 commit comments

Comments
 (0)