From 95cb5471849ce3573352715fc5f1a8da97baa65c Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 29 Feb 2020 23:26:54 +0800 Subject: [PATCH] feat: use pydantic to implement testcase schema --- httprunner/app/main.py | 4 +- httprunner/app/routers/debugtalk.py | 2 +- httprunner/app/routers/testcase.py | 63 --------------------- httprunner/schema/__init__.py | 1 + httprunner/schema/api.py | 14 +++++ httprunner/schema/common.py | 61 +++++++++++++++++++++ httprunner/schema/testcase.py | 85 +++++++++++++++++++++++++++++ pyproject.toml | 1 + tests/test_schema.py | 0 9 files changed, 165 insertions(+), 66 deletions(-) delete mode 100644 httprunner/app/routers/testcase.py create mode 100644 httprunner/schema/__init__.py create mode 100644 httprunner/schema/api.py create mode 100644 httprunner/schema/common.py create mode 100644 httprunner/schema/testcase.py create mode 100644 tests/test_schema.py diff --git a/httprunner/app/main.py b/httprunner/app/main.py index 171013d9..1f1139bc 100644 --- a/httprunner/app/main.py +++ b/httprunner/app/main.py @@ -1,7 +1,7 @@ from fastapi import FastAPI from httprunner import __version__ -from .routers import deps, debugtalk, testcase +from .routers import deps, debugtalk, debug app = FastAPI() @@ -19,4 +19,4 @@ async def get_hrun_version(): app.include_router(deps.router) app.include_router(debugtalk.router) -app.include_router(testcase.router) +app.include_router(debug.router) diff --git a/httprunner/app/routers/debugtalk.py b/httprunner/app/routers/debugtalk.py index 2ed2838b..c6d8f7b0 100644 --- a/httprunner/app/routers/debugtalk.py +++ b/httprunner/app/routers/debugtalk.py @@ -19,7 +19,7 @@ def stdout_io(stdout=None): sys.stdout = old -@router.post("/hrun/debug/debugtalk", tags=["debugtalk"]) +@router.post("/hrun/debug/debugtalk_py", tags=["debugtalk"]) async def debug_python(request: Request): body = await request.body() diff --git a/httprunner/app/routers/testcase.py b/httprunner/app/routers/testcase.py deleted file mode 100644 index ded7285c..00000000 --- a/httprunner/app/routers/testcase.py +++ /dev/null @@ -1,63 +0,0 @@ -from fastapi import APIRouter - -from httprunner.api import HttpRunner - -router = APIRouter() -runner = HttpRunner() - - -@router.get("/hrun/debug/api", tags=["testcase"]) -async def debug_single_api(): - pass - - -@router.get("/hrun/debug/testcase", tags=["testcase"]) -async def debug_single_testcase(): - resp = { - "code": 0, - "message": "success", - "result": {} - } - testcases = [ - { - "config": { - 'name': "post data", - 'variables': { - "var1": "abc", - "var2": "def" - }, - "export": ["status_code", "req_data"] - }, - "teststeps": [ - { - "name": "post data", - "request": { - "url": "http://httpbin.org/post", - "method": "POST", - "headers": { - "User-Agent": "python-requests/2.18.4", - "Content-Type": "application/json" - }, - "data": "$var1" - }, - "extract": { - "status_code": "status_code", - "req_data": "content.data" - }, - "validate": [ - {"eq": ["status_code", 201]} - ] - } - ] - } - ] - tests_mapping = { - "testcases": testcases - } - summary = runner.run_tests(tests_mapping) - if not summary["success"]: - resp["code"] = 1 - resp["message"] = "fail" - - resp["result"] = summary - return resp diff --git a/httprunner/schema/__init__.py b/httprunner/schema/__init__.py new file mode 100644 index 00000000..9009366e --- /dev/null +++ b/httprunner/schema/__init__.py @@ -0,0 +1 @@ +from .testcase import ProjectMeta, TestCase, TestCases diff --git a/httprunner/schema/api.py b/httprunner/schema/api.py new file mode 100644 index 00000000..855d2487 --- /dev/null +++ b/httprunner/schema/api.py @@ -0,0 +1,14 @@ +from pydantic import BaseModel + +from httprunner.schema import common + + +class Api(BaseModel): + name: common.Name + request: common.Request + variables: common.Variables + base_url: common.BaseUrl + setup_hooks: common.Hook + teardown_hooks: common.Hook + extract: common.Extract + validate: common.Validate diff --git a/httprunner/schema/common.py b/httprunner/schema/common.py new file mode 100644 index 00000000..7229488d --- /dev/null +++ b/httprunner/schema/common.py @@ -0,0 +1,61 @@ +from enum import Enum +from typing import Dict, List, Any, Tuple + +from pydantic import BaseModel, HttpUrl, Field + +Name = str +Url = HttpUrl +BaseUrl = str +Variables = Dict[str, Any] +Headers = Dict[str, str] +Verify = bool +Hook = List[str] +Export = List[str] +Extract = Dict[str, str] +Validate = List[Dict[str, Tuple[str, Any]]] +Env = Dict[str, Any] + + +class MethodEnum(str, Enum): + GET = 'GET' + POST = 'POST' + PUT = "PUT" + DELETE = "DELETE" + HEAD = "HEAD" + OPTIONS = "OPTIONS" + PATCH = "PATCH" + CONNECT = "CONNECT" + TRACE = "TRACE" + + +class TestsConfig(BaseModel): + name: Name + verify: Verify = False + base_url: BaseUrl = "" + variables: Variables = {} + setup_hooks: Hook = [] + teardown_hooks: Hook = [] + export: Export = [] + + class Config: + schema_extra = { + "examples": [ + { + "name": "used in testcase/testsuite to configure common fields", + "verify": False, + "base_url": "https://httpbin.org" + } + ] + } + + +class Request(BaseModel): + method: MethodEnum = MethodEnum.GET + url: Url + params: Dict[str, str] = {} + headers: Headers = {} + req_json: Dict = Field({}, alias="json") + cookies: Dict[str, str] = {} + timeout: int = 120 + allow_redirects: bool = True + verify: Verify = False diff --git a/httprunner/schema/testcase.py b/httprunner/schema/testcase.py new file mode 100644 index 00000000..4ead072e --- /dev/null +++ b/httprunner/schema/testcase.py @@ -0,0 +1,85 @@ +from typing import Dict, List, Text + +from pydantic import BaseModel, Field + +from httprunner.schema import common + + +class ProjectMeta(BaseModel): + debugtalk_py: Text = "" + variables: common.Variables = {} + env: common.Env = {} + + +class TestStep(BaseModel): + name: common.Name + request: common.Request + extract: Dict[str, str] = {} + validation: common.Validate = Field([], alias="validate") + + +class TestCase(BaseModel): + config: common.TestsConfig + teststeps: List[TestStep] + + class Config: + schema_extra = { + "examples": [ + { + "config": { + "name": "testcase name" + }, + "teststeps": [ + { + "name": "api 1", + "api": "/path/to/api1" + }, + { + "name": "api 2", + "api": "/path/to/api2" + } + ] + }, + { + "config": { + "name": "demo testcase", + "variables": { + "device_sn": "ABC", + "username": "${ENV(USERNAME)}", + "password": "${ENV(PASSWORD)}" + }, + "base_url": "http://127.0.0.1:5000" + }, + "teststeps": [ + { + "name": "demo step 1", + "api": "path/to/api1.yml", + "variables": { + "user_agent": "iOS/10.3", + "device_sn": "$device_sn" + }, + "extract": [ + { + "token": "content.token" + } + ], + "validate": [ + { + "eq": ["status_code", 200] + } + ] + }, + { + "name": "demo step 2", + "api": "path/to/api2.yml", + "variables": { + "token": "$token" + } + } + ] + } + ] + } + + +TestCases = List[TestCase] diff --git a/pyproject.toml b/pyproject.toml index a0140669..28311c3a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ filetype = "^1.0.5" jsonpath = "^0.82" sentry-sdk = "^0.13.5" jsonschema = "^3.2.0" +pydantic = "^1.4" [tool.poetry.dev-dependencies] flask = "<1.0.0" diff --git a/tests/test_schema.py b/tests/test_schema.py new file mode 100644 index 00000000..e69de29b