Skip to content

Commit fc95e98

Browse files
authored
Merge pull request #6 from ruudboon/implement-json-schema
Added `seeResponseIsValidOnJsonSchema`
2 parents 0b0c03e + d663dd5 commit fc95e98

File tree

9 files changed

+182
-13
lines changed

9 files changed

+182
-13
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: 55 additions & 12 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.
@@ -771,18 +773,7 @@ public function seeResponseIsJson()
771773
{
772774
$responseContent = $this->connectionModule->_getResponseContent();
773775
\PHPUnit\Framework\Assert::assertNotEquals('', $responseContent, 'response is empty');
774-
json_decode($responseContent);
775-
$errorCode = json_last_error();
776-
$errorMessage = json_last_error_msg();
777-
\PHPUnit\Framework\Assert::assertEquals(
778-
JSON_ERROR_NONE,
779-
$errorCode,
780-
sprintf(
781-
"Invalid json: %s. System message: %s.",
782-
$responseContent,
783-
$errorMessage
784-
)
785-
);
776+
$this->decodeAndValidateJson($responseContent);
786777
}
787778

788779
/**
@@ -841,6 +832,58 @@ public function seeResponseContainsJson($json = [])
841832
);
842833
}
843834

835+
/**
836+
* Checks whether last response matches the supplied json schema
837+
* Supply schema as json string
838+
*
839+
* @part json
840+
*/
841+
public function seeResponseIsValidOnJsonSchema($schema)
842+
{
843+
$responseContent = $this->connectionModule->_getResponseContent();
844+
\PHPUnit\Framework\Assert::assertNotEquals('', $responseContent, 'response is empty');
845+
$responseObject = $this->decodeAndValidateJson($responseContent);
846+
847+
\PHPUnit\Framework\Assert::assertNotEquals('', $schema, 'schema is empty');
848+
$schemaObject = $this->decodeAndValidateJson($schema, "Invalid schema json: %s. System message: %s.");
849+
850+
$validator = new JsonSchemaValidator();
851+
$validator->validate($responseObject, $schemaObject, JsonContraint::CHECK_MODE_VALIDATE_SCHEMA);
852+
$outcome = $validator->isValid();
853+
$error = "";
854+
if (!$outcome) {
855+
$errors = $validator->getErrors();
856+
$error = array_shift($errors)["message"];
857+
}
858+
\PHPUnit\Framework\Assert::assertTrue(
859+
$outcome,
860+
$error
861+
);
862+
}
863+
864+
/**
865+
* Converts string to json and asserts that no error occured while decoding.
866+
*
867+
* @param string $jsonString the json encoded string
868+
* @param string $errorFormat optional string for custom sprintf format
869+
*/
870+
protected function decodeAndValidateJson($jsonString, $errorFormat="Invalid json: %s. System message: %s.")
871+
{
872+
$json = json_decode($jsonString);
873+
$errorCode = json_last_error();
874+
$errorMessage = json_last_error_msg();
875+
\PHPUnit\Framework\Assert::assertEquals(
876+
JSON_ERROR_NONE,
877+
$errorCode,
878+
sprintf(
879+
$errorFormat,
880+
$jsonString,
881+
$errorMessage
882+
)
883+
);
884+
return $json;
885+
}
886+
844887
/**
845888
* Returns current response so that it can be used in next scenario steps.
846889
*
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
@@ -464,6 +464,29 @@ public function testHaveServerParameter()
464464
$this->assertArrayHasKey('my', $server);
465465
}
466466

467+
/**
468+
* @param $schema
469+
* @param $response
470+
* @param $outcome
471+
* @param $error
472+
*
473+
* @dataProvider schemaAndResponse
474+
*/
475+
476+
public function testSeeResponseIsValidOnJsonSchemachesJsonSchema($schema, $response, $outcome, $error) {
477+
478+
$response = file_get_contents(codecept_data_dir($response));
479+
$this->setStubResponse($response);
480+
481+
$validSchema = file_get_contents(codecept_data_dir($schema));
482+
483+
if (!$outcome) {
484+
$this->expectExceptionMessage($error);
485+
$this->shouldFail();
486+
}
487+
$this->module->seeResponseIsValidOnJsonSchema($validSchema);
488+
}
489+
467490
/**
468491
* @param $configUrl
469492
* @param $requestUrl
@@ -503,6 +526,17 @@ public function testRestExecute($configUrl, $requestUrl, $expectedFullUrl)
503526
$module->sendGET($requestUrl);
504527
}
505528

529+
public static function schemaAndResponse()
530+
{
531+
return [
532+
//schema, responsefile, valid
533+
['schemas/basic-schema.json', 'responses/valid-basic-schema.json', true, ""],
534+
['schemas/basic-schema.json', 'responses/invalid-basic-schema.json', false, "Must have a minimum value of 0"],
535+
['schemas/complex-schema.json', 'responses/valid-complex-schema.json', true, ""],
536+
['schemas/complex-schema.json', 'responses/invalid-complex-schema.json', false, "String value found, but a boolean is required"]
537+
];
538+
}
539+
506540
public static function configAndRequestUrls()
507541
{
508542
return [

0 commit comments

Comments
 (0)