diff --git a/README-ru.md b/README-ru.md index e4b1b43..37760ad 100644 --- a/README-ru.md +++ b/README-ru.md @@ -10,6 +10,7 @@ Gonkey протестирует ваши сервисы, используя их - моки для имитации внешних сервисов - можно подключить к проекту как библиотеку и запускать вместе с юнит-тестами - запись результата тестов в виде отчета [Allure](http://allure.qatools.ru/) +- имеется [JSON-schema](#json-schema) для автодополнения и валидации YAML-файлов Gonkey ## Содержание @@ -1455,3 +1456,71 @@ Example: - '{ "id": 2, "name": "John", "surname": "Doe" }' - '{ "id": 1, "name": "Jane", "surname": "Doe" }' ``` + +## JSON-schema +Для упрощения написания тестов на Gonkey, используйте [файл со схемой](https://raw.githubusercontent.com/lamoda/gonkey/master/gonkey.json) + +Он добавляет in-line документацию и авто-дополнение в IDE которые это поддерживают. + + +Пример работы в IDE Jetbrains: +![Example Jetbrains](https://i.imgur.com/oYuPuR3.gif) + +Пример работы в IDE VSCode: +![Example Jetbrains](https://i.imgur.com/hBIGjP9.gif) + + +### Настройка на IDE Jetbrains +Скачайте [файл со схемой](https://raw.githubusercontent.com/lamoda/gonkey/master/gonkey.json). +В настройках Languages & Frameworks > Schemas and DTDs > JSON Schema Mappings + +![Jetbrains IDE Settings](https://i.imgur.com/xkO22by.png) + +Добавьте новую схему + +![Add schema](https://i.imgur.com/XHw14GJ.png) + +Задайте имя схемы, выберите скачанный файл, а также выберите версию схем Draft 7 + +![Name, file, version](https://i.imgur.com/LfJfis0.png) + +После этого добавьте маппинг. Можно выбрать файл, папку с тестами или маску. + +![Mapping](https://i.imgur.com/iFjm0Ld.png) + +Выберите то что удобно для вас. + +![Mapping pattern](https://i.imgur.com/WIK6sZW.png) + +После этого сохраните настройки, и если вы всё сделали правильно в нижнем правом углу окна IDE не должно +отображатся No JSON Schema +![No Schema](https://i.imgur.com/zLqv1Zv.png) + +А должно быть ваше название схемы. + +![Schema Name](https://i.imgur.com/DDXdCO7.png) + +### Настройка на IDE VSCode + +Для начала вам нужно установить плагин для работы с YAML +Откройте меню Code(File)->Preferences->Extensions + +![VSCode Preferences](https://i.imgur.com/X7bk5Kh.png) + +Наберите в поиске YAML, и установите расширение YAML Language Support by Red Hat + +![Yaml Extension](https://i.imgur.com/57onioF.png) + +Откройте меню Code(File)->Preferences->Settings +Наберите YAML:Schemas и нажмите на ссылку _Edit in settings.json_ +![Yaml link](https://i.imgur.com/IEwxWyG.png) + +Добавьте маппинг файла и путь к схеме +``` +"yaml.schemas": { + "C:\\Users\\Leo\\gonkey.json": ["*.gonkey.yaml"] +} +``` + +В примере выше, схема из файла C:\Users\Leo\gonkey.json будет применяться ко всем файлам +с расширением .gonkey.yaml \ No newline at end of file diff --git a/README.md b/README.md index 7b2e31e..84c4512 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Capabilities: - provides mocks for external services - can be used as a library and ran together with unit-tests - stores the results as an [Allure](http://allure.qatools.ru/) report +- there is a [JSON-schema](#json-schema) to add autocomletion and validation for gonkey YAML files ## Table of contents @@ -1455,3 +1456,73 @@ Example: - '{ "id": 2, "name": "John", "surname": "Doe" }' - '{ "id": 1, "name": "Jane", "surname": "Doe" }' ``` + +## JSON-schema +Use [file with schema](https://raw.githubusercontent.com/lamoda/gonkey/master/gonkey.json) to add syntax highlight to your favourite IDE and write Gonkey tests more easily. + +It adds in-line documentation and auto-completion to any IDE that supports it. + + + +Example in Jetbrains IDE: +![Example Jetbrains](https://i.imgur.com/oYuPuR3.gif) + +Example in VSCode IDE: +![Example Jetbrains](https://i.imgur.com/hBIGjP9.gif) + + +### Setup in Jetbrains IDE +Download [file with schema](https://raw.githubusercontent.com/lamoda/gonkey/master/gonkey.json). +Open preferences File->Preferences +In Languages & Frameworks > Schemas and DTDs > JSON Schema Mappings + +![Jetbrains IDE Settings](https://i.imgur.com/xkO22by.png) + +Add new schema + +![Add schema](https://i.imgur.com/XHw14GJ.png) + +Specify schema name, schema file, and select Schema version: Draft 7 + +![Name, file, version](https://i.imgur.com/LfJfis0.png) + +After that add mapping. You can choose from single file, directory, or file mask. + +![Mapping](https://i.imgur.com/iFjm0Ld.png) + +Choose what suits you best. + +![Mapping pattern](https://i.imgur.com/WIK6sZW.png) + +Save your preferences. If you done everything right, you should not see No JSON Schema in bottom right corner + +![No Schema](https://i.imgur.com/zLqv1Zv.png) + +Instead, you should see your schema name + +![Schema Name](https://i.imgur.com/DDXdCO7.png) + +### Setup is VSCode IDE + +At first, you need to download YAML Language plugin +Open Extensions by going to Code(File)->Preferences->Extensions + +![VSCode Preferences](https://i.imgur.com/X7bk5Kh.png) + +Look for YAML and install YAML Language Support by Red Hat + +![Yaml Extension](https://i.imgur.com/57onioF.png) + +Open Settings by going to Code(File)->Preferences->Settings + +Open Schema Settings by typing YAML:Schemas and click on _Edit in settings.json_ +![Yaml link](https://i.imgur.com/IEwxWyG.png) + +Add file match to apply the JSON on YAML files. +``` +"yaml.schemas": { + "C:\\Users\\Leo\\gonkey.json": ["*.gonkey.yaml"] +} +``` + +In the example above the JSON schema stored in C:\Users\Leo\gonkey.json will be applied on all the files that ends with .gonkey.yaml \ No newline at end of file diff --git a/gonkey.json b/gonkey.json new file mode 100644 index 0000000..273d9ad --- /dev/null +++ b/gonkey.json @@ -0,0 +1,551 @@ +{ + "$id": "https://raw.githubusercontent.com/lamoda/gonkey/master/gonkey.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Gonkey test automation tool", + "description": "Gonkey described here https://github.com/lamoda/gonkey", + "anyOf": [ + { "$ref": "#/$defs/gonkeyTest" }, + { + "type": "array", + "items": { "$ref": "#/$defs/gonkeyTest" } + } + ], + "$defs": { + "gonkeyTest": { + "type": "object", + "properties": { + "name":{ + "type": "string", + "description": "test name" + }, + "method": { + "type": "string", + "description": "HTTP request type", + "enum": [ + "GET", + "POST", + "HEAD", + "PUT", + "DELETE", + "OPTIONS", + "TRACE", + "PATCH", + "CONNECT" + ] + }, + "path":{ + "type": "string", + "description": "HTTP request path" + }, + "query":{ + "type": "string", + "description": "HTTP request query" + }, + "headers":{ + "$ref": "#/$defs/headers" + }, + "cookies":{ + "type":"object", + "description": "map of HTTP request cookies", + "additionalProperties": { "type": "string" } + }, + "fixtures":{ + "type": "array", + "description": "a list of strings, containing paths to database fixtures", + "items": {"type":"string"} + }, + "comparisonParams":{ + "type":"object", + "description": "Boolean switches to conrol response checks", + "properties": { + "ignoreValues": { "type": "boolean", "description": "Ignore response body JSON values, validate only parameters names" }, + "DisallowExtraFields": { "type": "boolean", "description": "Disallow extra JSON parameters in response body" }, + "ignoreArraysOrdering": { "type": "boolean", "description": "Ignore JSON arrays elements ordering in response body" }, + "ignoreDbOrdering ": { "type": "boolean", "description": "Toggles ignore ordering in DB response" } + + } + }, + "status": { + "type": "string", + "description": "Test status", + "anyOf": [ + { + "const": "focus", + "title": "run only this specific test, and mark all other tests with unset status as skipped" + }, + { + "const": "broken", + "title": "do not run test, only mark it as broken" + }, + { + "const": "skipped", + "title": "do not run test, skip it" + } + ] + }, + "mocks":{ + "type":"object", + "description": "map of service mocks", + "additionalProperties": {"$ref": "#/$defs/mock"} + }, + "beforeScript": { + "type":"object", + "description": "script, that executes after mocks setup and before the HTTP-request is sent", + "properties": { + "path": { + "type": "string", + "description": "string with a path to the script file." + }, + "timeout ": { + "type": "integer", + "description": "time in seconds, until stopping the script on timeout. The default value is 3" + } + }, + "required": ["path"] + }, + "afterRequestScript": { + "type":"object", + "description": "script, that executes after the HTTP-request was sent", + "properties": { + "path": { + "type": "string", + "description": "string with a path to the script file." + }, + "timeout ": { + "type": "integer", + "description": "time in seconds, until stopping the script on timeout. The default value is 3" + } + }, + "required": ["path"] + }, + "dbQuery":{ + "type": "string", + "description": "a string that contains an SQL query" + }, + "dbResponse":{ + "type": "array", + "description": "a list of strings, containing JSON objects that the DB request should return", + "items": {"type":"string"} + }, + "variables":{ + "type":"object", + "description": "map of strings that substituted in placeholders. example of placeholder: {{ $my_variable }}" + }, + "request":{ + "type":"string", + "description": "string that contains HTTP request body" + }, + "response":{ + "type":"object", + "description": "numeric HTTP response code (i.e. 200:) with desired response body" + }, + "cases":{ + "type": "array", + "description": "a list of cases, containing parameters to substitute into variables", + "items": { + "type":"object", + "properties":{ + "requestArgs": {"$ref": "#/$defs/requestArgs"}, + "responseArgs": {"$ref": "#/$defs/responseArgs"}, + "dbQueryArgs": {"$ref": "#/$defs/dbQueryArgs"}, + "dbResponseArgs": {"$ref": "#/$defs/requestArgs"} + } + } + } + } + }, + "statusCode": { + "type": "integer", + "description": "HTTP-code of the response, the default value is 200" + }, + "dbQueryArgs":{ + "type":"object", + "description": "map of database request parametrization arguments" + }, + "dbResponseArgs":{ + "type":"object", + "description": "map of database response parametrization arguments" + }, + "requestArgs":{ + "type":"object", + "description": "map of HTTP request parametrization arguments" + }, + "responseArgs":{ + "type":"object", + "description": "numeric HTTP response code (i.e. 200:) with map of parametrization arguments" + }, + "headers":{ + "type":"object", + "description": "map of HTTP request headers", + "additionalProperties": { "type": "string" } + }, + "mock":{ + "type": "object", + "required": ["strategy"], + "properties": { + "strategy": { + "type": "string", + "description": "mock strategy", + "anyOf": [ + { + "const": "nop", + "title": "Empty strategy. All requests are served with 204 No Content and empty body. No parameters." + }, + { + "const": "file", + "title": "Returns a response read from a file." + }, + { + "const": "constant", + "title": "Returns a defined response." + }, + { + "const": "template", + "title": "This strategy gives ability to use incoming request data into mock response. Implemented with package text/template. Automatically preload incoming request into variable named request" + }, + { + "const": "uriVary", + "title": "Uses different response strategies, depending on a path of a requested resource." + }, + { + "const": "methodVary", + "title": "Uses various response strategies, depending on the request method." + }, + { + "const": "sequence", + "title": "With this strategy for each consequent request you will get a reply defined by a consequent nested strategy." + } + ] + }, + "calls": { + "type": "integer", + "description": "how many times each mock or mock resource must be called" + }, + "requestConstraints": { + "description": "list of mock request constraints", + "type": "array", + "items": { + "$ref": "#/$defs/requestConstraint" + } + } + }, + "allOf": [ + { + "if": { + "properties": { "strategy": { "const": "file" } } + }, + "then": { + "properties": { + "filename": { + "type": "string", + "description": "name of the file that contains the response body" + }, + "statusCode": { + "$ref": "#/$defs/statusCode" + }, + "headers":{ + "$ref": "#/$defs/headers" + } + }, + "required": ["filename"] + } + }, + { + "if": { + "properties": { "strategy": { "const": "constant" } } + }, + "then": { + "properties": { + "body": { + "type": "string", + "description": "sets the response body" + }, + "statusCode": { + "$ref": "#/$defs/statusCode" + }, + "headers":{ + "$ref": "#/$defs/headers" + } + }, + "required": ["body"] + } + }, + { + "if": { + "properties": { "strategy": { "const": "template" } } + }, + "then": { + "properties": { + "body": { + "type": "string", + "description": "sets the response body, must be valid text/template string" + }, + "statusCode": { + "$ref": "#/$defs/statusCode" + }, + "headers":{ + "$ref": "#/$defs/headers" + } + }, + "required": ["body"] + } + }, + { + "if": { + "properties": { "strategy": { "const": "uriVary" } } + }, + "then": { + "properties": { + "uris": { + "description": "a map of resources, each resource can be configured as a separate mock-service using any available request constraints and response strategies", + "$ref": "#/$defs/gonkeyTest/properties/mocks" + }, + "basePath ":{ + "type": "string", + "description": "common base route for all resources, empty by default" + } + }, + "required": ["uris"] + } + }, + { + "if": { + "properties": { "strategy": { "const": "methodVary" } } + }, + "then": { + "properties": { + "methods": { + "description": "a map of methods (GET, POST, etc), each method can be configured as a separate mock-service using any available request constraints and response strategies", + "$ref": "#/$defs/gonkeyTest/properties/mocks" + } + }, + "required": ["methods"] + } + }, + { + "if": { + "properties": { "strategy": { "const": "sequence" } } + }, + "then": { + "properties": { + "sequence": { + "description": "list of nested mock strategies", + "type": "array", + "items": { + "$ref": "#/$defs/mock" + } + } + }, + "required": ["sequence"] + } + } + ] + }, + "requestConstraint":{ + "type": "object", + "required": ["kind"], + "properties": { + "kind": { + "type": "string", + "description": "request constraint type", + "anyOf": [ + { + "const": "nop", + "title": "Empty constraint. Always successful. No parameters." + }, + { + "const": "bodyMatchesJSON", + "title": "Checks that the request body is JSON, and it corresponds to the JSON defined in the body parameter." + }, + { + "const": "bodyJSONFieldMatchesJSON", + "title": "When request body is JSON, checks that value of particular JSON-field is string-packed JSON that matches to JSON defined in value parameter" + }, + { + "const": "pathMatches", + "title": "Checks that the request path corresponds to the expected one." + }, + { + "const": "queryMatches", + "title": "Checks that the GET request parameters correspond to the ones defined in the query parameter." + }, + { + "const": "queryMatchesRegexp", + "title": "Checks that the GET request parameters correspond regexp pattern." + }, + { + "const": "methodIs", + "title": "Checks that the request method corresponds to the expected one." + }, + { + "const": "methodIsGET", + "title": "Checks that the request method corresponds to GET" + }, + { + "const": "methodIsPOST", + "title": "Checks that the request method corresponds to POST" + }, + { + "const": "headerIs", + "title": "Checks that the request has the defined header and (optional) that its value either equals the pre-defined one or falls under the definition of a regular expression." + }, + { + "const": "bodyMatchesText", + "title": "Checks that the request has the defined body text, or it falls under the definition of a regular expression." + }, + { + "const": "bodyMatchesXML", + "title": "Checks that the request body is XML, and it matches to the XML defined in the body parameter." + } + ] + } + }, + "allOf": [ + { + "if": { + "properties": { "kind": { "const": "bodyMatchesJSON" } } + }, + "then": { + "properties": { + "body": { + "type": "string", + "description": "expected JSON. All keys on all levels defined in this parameter must be present in the request body." + } + }, + "required": ["body"] + } + }, + { + "if": { + "properties": { "kind": { "const": "bodyJSONFieldMatchesJSON" } } + }, + "then": { + "properties": { + "path": { + "type": "string", + "description": "path to string field, containing JSON to check" + }, + "value": { + "type": "string", + "description": "expected JSON" + } + }, + "required": ["path","value"] + } + }, + { + "if": { + "properties": { "kind": { "const": "pathMatches" } } + }, + "then": { + "properties": { + "path": { + "type": "string", + "description": "a string with the expected request path value" + }, + "regexp": { + "type": "string", + "description": "a regular expression to check the path value against" + } + } + } + }, + { + "if": { + "properties": { "kind": { "const": "queryMatches" } } + }, + "then": { + "properties": { + "expectedQuery": { + "type": "string", + "description": "a list of parameters to compare the parameter string to. The order of parameters is not important" + } + }, + "required": ["expectedQuery"] + } + }, + { + "if": { + "properties": { "kind": { "const": "queryMatchesRegexp" } } + }, + "then": { + "properties": { + "expectedQuery": { + "type": "string", + "description": "a list of parameters to compare the parameter string to. The order of parameters is not important" + } + }, + "required": ["expectedQuery"] + } + }, + { + "if": { + "properties": { "kind": { "const": "methodIs" } } + }, + "then": { + "properties": { + "method": { + "type": "string", + "description": "string to compare the request method to" + } + }, + "required": ["method"] + } + }, + { + "if": { + "properties": { "kind": { "const": "headerIs" } } + }, + "then": { + "properties": { + "header": { + "type": "string", + "description": "name of the header that is expected with the request" + }, + "value": { + "type": "string", + "description": "a string with the expected request header value" + }, + "regexp": { + "type": "string", + "description": "a regular expression to check the header value against" + } + }, + "required": ["header"] + } + }, + { + "if": { + "properties": { "kind": { "const": "bodyMatchesText" } } + }, + "then": { + "properties": { + "body": { + "type": "string", + "description": "a string with the expected request body value" + }, + "regexp": { + "type": "string", + "description": "a regular expression to check the body value against" + } + } + } + }, + { + "if": { + "properties": { "kind": { "const": "bodyMatchesXML" } } + }, + "then": { + "properties": { + "body": { + "type": "string", + "description": "a string with the expected request body value" + } + }, + "required": ["body"] + } + } + ] + } + } +} \ No newline at end of file