Skip to content

Commit

Permalink
🎉 SDK 准备就绪 (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
wu-clan authored Mar 12, 2024
1 parent 0e6b019 commit eaaf38a
Show file tree
Hide file tree
Showing 35 changed files with 500 additions and 437 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ httpfpt/log/
.idea/
httpfpt/data/online*
.ruff_cache/
testcases/
17 changes: 6 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
![GitHub release (with filter)](https://img.shields.io/github/v/release/wu-clan/httpfpt)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

> [!IMPORTANT]
> 当前分支为 SDK 版本,如需修改源码进行功能定制,建议切换到 [master](https://github.com/wu-clan/httpfpt) 分支
基于 HTTP 请求的快速数据驱动 pytest 接口自动化测试框架

我在掘金发表了关于 `HttpFpt` 的前身和由来,包括部分功能点的说明, 感兴趣
Expand Down Expand Up @@ -37,29 +40,21 @@

## ⬇️ 下载

克隆:

```shell
git clone https://github.com/wu-clan/httpfpt.git
pip install httpfpt
```

## 🧑‍💻 Use

1. 安装依赖:

```shell
pip install -r requirements.txt
```

2. 安装 redis 数据库并启动服务
1. 安装 redis 数据库并启动服务

[Redis Windows](https://github.com/redis-windows/redis-windows)

[Linux / macOS](https://redis.io/download/)

[Docker](https://hub.docker.com/_/redis)

3. 安装 mysql 数据库(可选,如果你需要本地数据库)
2. 安装 mysql 数据库(可选,如果你需要本地数据库)

[Windows / Linux / macOS](https://dev.mysql.com/downloads/installer/)

Expand Down
63 changes: 23 additions & 40 deletions httpfpt/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,13 @@
import cappa

from cappa import Subcommands
from rich.traceback import install as rich_install
from typing_extensions import TYPE_CHECKING, Annotated
from typing_extensions import Annotated

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from httpfpt.run import run
from httpfpt.utils.cli.about_testcase import generate_testcases, testcase_data_verify
from httpfpt.utils.cli.import_case_data import (
import_apifox_case_data,
import_git_case_data,
import_har_case_data,
import_jmeter_case_data,
import_openapi_case_data,
import_postman_case_data,
)
from httpfpt.utils.cli.version import get_version
from httpfpt.utils.rich_console import console

if TYPE_CHECKING:
from cappa.parser import Value


def cmd_run_test_parse(value: Value) -> bool | Value:
"""运行测试命令参数解析"""
if len(value) == 0: # type: ignore
return True
else:
return value
from httpfpt.utils.cli.new_project import create_new_project
from httpfpt.utils.cli.version import get_version


@cappa.command(name='httpfpt-cli')
Expand All @@ -52,29 +31,23 @@ class HttpFptCLI:
help='Print version information.',
),
]
run_test: Annotated[
list[str] | None,
start_project: Annotated[
bool,
cappa.Arg(
value_name='<PYTEST ARGS / NONE>',
short='-r',
long='--run',
default=None,
help='Run test cases, do not support use with other commands, but support custom pytest running parameters,'
' default parameters see `httpfpt/run.py`.',
parse=cmd_run_test_parse,
num_args=-1,
value_name='<PROJECT NAME, PROJECT PATH>',
long='--startproject',
default=False,
help='Create a new project.',
required=False,
),
]
subcmd: Subcommands[TestCaseCLI | ImportCLI | None] = None

def __call__(self) -> None:
if self.version:
get_version()
if self.run_test:
if self.version or self.subcmd:
console.print('\n❌ 暂不支持 -r/--run 命令与其他 CLI 命令同时使用')
raise cappa.Exit(code=1)
run(*self.run_test) if isinstance(self.run_test, list) else run()
if self.start_project:
create_new_project()


@cappa.command(name='testcase', help='Test case tools.')
Expand Down Expand Up @@ -103,6 +76,8 @@ class TestCaseCLI:
]

def __call__(self) -> None:
from httpfpt.utils.cli.about_testcase import generate_testcases, testcase_data_verify

if self.data_verify:
testcase_data_verify(self.data_verify)
if self.generate:
Expand Down Expand Up @@ -176,6 +151,15 @@ class ImportCLI:
]

def __call__(self) -> None:
from httpfpt.utils.cli.import_case_data import (
import_apifox_case_data,
import_git_case_data,
import_har_case_data,
import_jmeter_case_data,
import_openapi_case_data,
import_postman_case_data,
)

if self.openai:
import_openapi_case_data(self.openai)
if self.apifox:
Expand All @@ -192,7 +176,6 @@ def __call__(self) -> None:

def cappa_invoke() -> None:
"""cli 执行程序"""
rich_install()
cappa.invoke(HttpFptCLI)


Expand Down
7 changes: 7 additions & 0 deletions httpfpt/common/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ def __str__(self) -> str:
return self.msg


class ConfigInitError(HttpFptErrorMixin, RuntimeError):
"""配置初始化错误"""

def __init__(self, msg: str) -> None:
super().__init__(msg)


class AuthError(HttpFptErrorMixin, ValueError):
"""认证错误"""

Expand Down
4 changes: 2 additions & 2 deletions httpfpt/common/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from loguru import logger

from httpfpt.core.path_conf import LOG_PATH
from httpfpt.core.path_conf import httpfpt_path

if TYPE_CHECKING:
import loguru
Expand All @@ -28,7 +28,7 @@ def log() -> loguru.Logger:
:return:
"""
log_path = LOG_PATH
log_path = httpfpt_path.log_dir

if not os.path.join(log_path):
os.makedirs(log_path)
Expand Down
22 changes: 11 additions & 11 deletions httpfpt/common/send_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from httpfpt.common.errors import AssertError, SendRequestError
from httpfpt.common.log import log
from httpfpt.core.get_conf import config
from httpfpt.core.get_conf import httpfpt_config
from httpfpt.db.mysql_db import mysql_client
from httpfpt.enums.request.body import BodyType
from httpfpt.enums.request.engin import EnginType
Expand Down Expand Up @@ -63,11 +63,11 @@ def _requests_engin(**kwargs) -> RequestsResponse:
:param kwargs:
:return:
"""
kwargs['timeout'] = kwargs['timeout'] or config.REQUEST_TIMEOUT
kwargs['verify'] = kwargs['verify'] or config.REQUEST_VERIFY
kwargs['proxies'] = kwargs['proxies'] or config.REQUEST_PROXIES_REQUESTS
kwargs['allow_redirects'] = kwargs['allow_redirects'] or config.REQUEST_REDIRECTS
request_retry = kwargs['retry'] or config.REQUEST_RETRY
kwargs['timeout'] = kwargs['timeout'] or httpfpt_config.REQUEST_TIMEOUT
kwargs['verify'] = kwargs['verify'] or httpfpt_config.REQUEST_VERIFY
kwargs['proxies'] = kwargs['proxies'] or httpfpt_config.REQUEST_PROXIES_REQUESTS
kwargs['allow_redirects'] = kwargs['allow_redirects'] or httpfpt_config.REQUEST_REDIRECTS
request_retry = kwargs['retry'] or httpfpt_config.REQUEST_RETRY
del kwargs['retry']
# 消除安全警告
requests.packages.urllib3.disable_warnings() # type: ignore
Expand All @@ -93,11 +93,11 @@ def _httpx_engin(**kwargs) -> HttpxResponse:
:param kwargs:
:return:
"""
kwargs['timeout'] = kwargs['timeout'] or config.REQUEST_TIMEOUT
verify = kwargs['verify'] or config.REQUEST_VERIFY
proxies = kwargs['proxies'] or config.REQUEST_PROXIES_HTTPX
redirects = kwargs['allow_redirects'] or config.REQUEST_REDIRECTS
request_retry = kwargs['retry'] or config.REQUEST_RETRY
kwargs['timeout'] = kwargs['timeout'] or httpfpt_config.REQUEST_TIMEOUT
verify = kwargs['verify'] or httpfpt_config.REQUEST_VERIFY
proxies = kwargs['proxies'] or httpfpt_config.REQUEST_PROXIES_HTTPX
redirects = kwargs['allow_redirects'] or httpfpt_config.REQUEST_REDIRECTS
request_retry = kwargs['retry'] or httpfpt_config.REQUEST_RETRY
del kwargs['verify']
del kwargs['proxies']
del kwargs['allow_redirects']
Expand Down
8 changes: 4 additions & 4 deletions httpfpt/common/yaml_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import yaml

from httpfpt.common.log import log
from httpfpt.core.path_conf import TEST_DATA_PATH, YAML_REPORT_PATH
from httpfpt.core.path_conf import httpfpt_path
from httpfpt.utils.time_control import get_current_time


Expand Down Expand Up @@ -78,7 +78,7 @@ def write_yaml_report(
:param mode: 文件写入模式
:return
"""
_yaml_report_path = YAML_REPORT_PATH
_yaml_report_path = httpfpt_path.yaml_report_dir
if not os.path.exists(_yaml_report_path):
os.makedirs(_yaml_report_path)
_file = os.path.join(_yaml_report_path, filename)
Expand All @@ -99,9 +99,9 @@ def write_yaml_vars(data: dict) -> None:
:param data:
:return:
"""
_file = os.path.join(TEST_DATA_PATH, 'global_vars.yaml')
_file = os.path.join(httpfpt_path.data_path, 'global_vars.yaml')
try:
_vars = read_yaml(TEST_DATA_PATH, filename='global_vars.yaml')
_vars = read_yaml(httpfpt_path.data_path, filename='global_vars.yaml')
_vars.update(data)
with open(_file, encoding='utf-8', mode='w') as f:
yaml.dump(_vars, f, allow_unicode=True)
Expand Down
8 changes: 4 additions & 4 deletions httpfpt/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from httpfpt.common.log import log
from httpfpt.common.variable_cache import variable_cache
from httpfpt.common.yaml_handler import write_yaml_report
from httpfpt.core.get_conf import config as sys_config
from httpfpt.core.get_conf import httpfpt_config


@pytest.fixture(scope='session', autouse=True)
Expand Down Expand Up @@ -62,7 +62,7 @@ def pytest_configure(config):
if metadata:
from pytest_metadata.plugin import metadata_key

config.stash[metadata_key]['Project Name'] = sys_config.PROJECT_NAME
config.stash[metadata_key]['Project Name'] = httpfpt_config.PROJECT_NAME
del config.stash[metadata_key]['Packages']
del config.stash[metadata_key]['Platform']
del config.stash[metadata_key]['Plugins']
Expand All @@ -76,7 +76,7 @@ def pytest_html_results_summary(prefix):
:return:
"""
# 向 html 报告中的 summary 添加额外信息
prefix.extend([html.p(f'Tester: {sys_config.TESTER_NAME}')])
prefix.extend([html.p(f'Tester: {httpfpt_config.TESTER_NAME}')])


@pytest.mark.optionalhook
Expand All @@ -87,7 +87,7 @@ def pytest_html_report_title(report):
:param report:
:return:
"""
report.title = f'{sys_config.TEST_REPORT_TITLE}'
report.title = f'{httpfpt_config.TEST_REPORT_TITLE}'


@pytest.mark.optionalhook
Expand Down
Loading

0 comments on commit eaaf38a

Please sign in to comment.