自动化测试架构搭建

自动化测试,资深测试老鸟总结自动化测试框架,从0到1的自动化测试框架搭建(详全) 每一个测试人员都应该了解每种框架的优缺点,以帮助你的团队更好地确定最适合的测试的框架,以达到事半功倍。根据需求使用适配的自动化测试框架有助于加快测试过程,并消除人为错误。它还使测试维护更加容易,加快测试进度,节省成本、时间和精力。此外,框架QA团队能够充分开发、执行和报告测试过程,同时还使代码可在多种情况下重用。为理想而奋斗时,往往会遇到许许多多的坎坷与挫折,正因为会遇到坎坷与挫折,所以有的人根本不为自己的理想而奋斗。你想想:行动可能不会给你带来快乐,但不行动决对不会给你带来快乐。 阅读详情

目录

组件架构

功能流程

项目结构

核心代码

测试结果

注解:

重要的第三方库

1. PyYAML

2. requests

3. deepdiff

4. tenacity

5. coloredlogs

6. colored

依赖汇总 (requirements.txt)


组件架构

功能流程

 

项目结构

ftfw/
├── requirements.txt
├── ftfw/
│   ├── __init__.py
│   ├── cli.py        # 命令行入口
│   ├── executor.py   # 测试执行器
│   ├── reporter.py   # 报告生成
│   └── utils/
│       ├── http.py
│       └── validator.py
ft/
└── server_test/
    ├── testconfig.yaml
    ├── Test_0001.yaml
    └── Test_0002.yaml

核心代码

cli.py

# -*- coding: utf-8 -*-
import argparse
import logging
import time
from pathlib import Path
from .executor import TestExecutor
from .reporter import ConsoleReporter

def configure_logging():
    logging.basicConfig(
        level=logging.DEBUG,
        format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    )

def main():
    configure_logging()
    parser = argparse.ArgumentParser(description="Microservice Test Framework")
    parser.add_argument(
        "testsuites",
        nargs="*",
        help="Name(s) of test suite directories"
    )
    args = parser.parse_args()
    logging.debug(f"Received testsuites: {args.testsuites}")

    base_path = Path("./ft")

    # 如果没传参数,自动扫描所有测试文件夹
    if not args.testsuites:
        tests_to_run = [p.name for p in base_path.iterdir() if p.is_dir()]
        logging.info(f"No testsuite specified, auto-discovered: {tests_to_run}")
    else:
        tests_to_run = args.testsuites

    overall_results = {
        "passed": {},
        "failed": {},
        "start_time": time.strftime("%Y-%m-%d %H:%M:%S"),
        "duration": 0
    }

    start_time = time.time()

    try:
        for suite_name in tests_to_run:
            suite_path = base_path / suite_name
            logging.info(f"Begin run testsuite: {suite_path}")

            executor = TestExecutor(suite_path)
            if not executor.service_check():
                logging.error(f"Service check failed for {suite_name}, skipping")
                overall_results["failed"][suite_name] = "Service check failed"
                continue

            for case in executor.config["testcase"]:
                logging.debug(f"Execute testcase {case} in suite {suite_name}")
                success, details = executor.execute_testcase(case)
                ConsoleReporter.print_case_result(case, success, details)

                if success:
                    overall_results["passed"][f"{suite_name}/{case}"] = details
                else:
                    overall_results["failed"][f"{suite_name}/{case}"] = "\n".join(details)

        overall_results["duration"] = time.time() - start_time
        ConsoleReporter.generate_summary(overall_results)

        return 0 if not overall_results["failed"] else 1

    except Exception as e:
        logging.error(f"Critical error occurred: {str(e)}")
        return 2


if __name__ == "__main__":
    raise SystemExit(main())

executor.py   # 测试执行器

# -*- coding: utf-8 -*-
import yaml
import logging
from pathlib import Path
from typing import Dict, List, Tuple
from .utils.http import HttpClient
from .utils.validator import ResponseValidator

class TestExecutor:
    def __init__(self, suite_path: Path):
        self.suite_path = suite_path
        self.http = HttpClient()
        self.logger = logging.getLogger("executor")
        self.config = self._load_config()
        self._validate_paths()

    def _load_config(self) -> Dict:
        """加载测试套件配置"""
        config_file = self.suite_path / "testconfig.yaml"
        with open(config_file) as f:
            config = yaml.safe_load(f)
        
        if config["testsuit_name"] != self.suite_path.name:
            self.logger.error(f"Test suite name mismatch, please check testsuit_name and files")
            raise ValueError("Test suite name mismatch")
        
        return config

    def _validate_paths(self):
        """验证测试用例文件存在"""
        missing = [f for f in self.config["testcase"] 
                  if not (self.suite_path / f"{f}.yaml").exists()]
        if missing:
            raise FileNotFoundError(f"Missing test cases: {missing}")

    def service_check(self) -> bool:
        """服务健康检查"""
        services = {
            "Front": self.config["Front"],
            "Backend": self.config["back_end"]
        }
        
        all_healthy = True
        for name, service in services.items():
            url = f"http://{service}"
            if self.http.check_service_health(url):
                self.logger.info(f"✅ {name} service healthy")
            else:
                self.logger.error(f"❌ {name} service unavailable")
                all_healthy = False
        return all_healthy

    def execute_testcase(self, case_name: str) -> Tuple[bool, List[str]]:
        """执行单个测试用例"""
        case_path = self.suite_path / f"{case_name}.yaml"
        with open(case_path) as f:
            case = yaml.safe_load(f)
        
        self.logger.debug(f"case description: {case['description']}")
        
        try:
            # 发送请求
            resp = self.http.send_request(
                method=case["request"]["method"],
                url=f"https://{self.config['back_end']}{case['request']['path']}",
                headers=case["request"].get("headers"),
                body=case["request"].get("body")
            )
        except RuntimeError as e:
            return False, [str(e)]
        self.logger.debug(f"get response as below code: {resp.status_code},data:{resp.content}")
        # 结果验证
        failures = []
        status_msg = ResponseValidator.status_code_validate(
            case["expected"]["status_code"],
            resp.status_code
        )
        if status_msg:
            failures.append(status_msg)
        if "data" in case["expected"]:
            try:
                data_diff = ResponseValidator.validate(
                    case["expected"]["data"],
                    resp.json()
                )
                failures.extend(data_diff)
            except ValueError:
                failures.append("Invalid JSON response")

        return len(failures) == 0, failures

reporter.py 

# -*- coding: utf-8 -*-
import argparse
import logging
import time
from pathlib import Path
from .executor import TestExecutor
from .reporter import ConsoleReporter

def configure_logging():
    logging.basicConfig(
        level=logging.DEBUG,
        format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    )

def main():
    configure_logging()
    parser = argparse.ArgumentParser(description="Microservice Test Framework")
    parser.add_argument(
        "testsuites",
        nargs="*",
        help="Name(s) of test suite directories"
    )
    args = parser.parse_args()
    logging.debug(f"Received testsuites: {args.testsuites}")

    base_path = Path("./ft")

    # 如果没传参数,自动扫描所有测试文件夹
    if not args.testsuites:
        tests_to_run = [p.name for p in base_path.iterdir() if p.is_dir()]
        logging.info(f"No testsuite specified, auto-discovered: {tests_to_run}")
    else:
        tests_to_run = args.testsuites

    overall_results = {
        "passed": {},
        "failed": {},
        "start_time": time.strftime("%Y-%m-%d %H:%M:%S"),
        "duration": 0
    }

    start_time = time.time()

    try:
        for suite_name in tests_to_run:
            suite_path = base_path / suite_name
            logging.info(f"Begin run testsuite: {suite_path}")

            executor = TestExecutor(suite_path)
            if not executor.service_check():
                logging.error(f"Service check failed for {suite_name}, skipping")
                overall_results["failed"][suite_name] = "Service check failed"
                continue

            for case in executor.config["testcase"]:
                logging.debug(f"Execute testcase {case} in suite {suite_name}")
                success, details = executor.execute_testcase(case)
                ConsoleReporter.print_case_result(case, success, details)

                if success:
                    overall_results["passed"][f"{suite_name}/{case}"] = details
                else:
                    overall_results["failed"][f"{suite_name}/{case}"] = "\n".join(details)

        overall_results["duration"] = time.time() - start_time
        ConsoleReporter.generate_summary(overall_results)

        return 0 if not overall_results["failed"] else 1

    except Exception as e:
        logging.error(f"Critical error occurred: {str(e)}")
        return 2


if __name__ == "__main__":
    raise SystemExit(main())

http.py

# -*- coding: utf-8 -*-
import requests
import logging
from typing import Dict, Any
from requests.exceptions import RequestException
from tenacity import retry, stop_after_attempt, wait_fixed

class HttpClient:
    def __init__(self, base_timeout: int = 10):
        self.session = requests.Session()
        self.base_timeout = base_timeout
        self.logger = logging.getLogger("myhttp")

    @retry(stop=stop_after_attempt(3), wait=wait_fixed(1))
    def send_request(
        self,
        method: str,
        url: str,
        headers: Dict[str, str] = None,
        body: Dict[str, Any] = None
    ) -> requests.Response:
        """带重试机制的请求发送"""
        try:
            self.logger.debug(f"Sending {method.upper()} request to {url} with headers={headers} and body={body}")
            return self.session.request(
                method=method.upper(),
                url=url,
                headers=headers or {},
                json=body,
                timeout=self.base_timeout
            )
        except RequestException as e:
            raise RuntimeError(f"Request failed: {str(e)}") from e

    def check_service_health(self, endpoint: str) -> bool:
        """服务健康检查"""
        try:
            resp = self.send_request("GET", f"{endpoint}")
            
            return resp.status_code in  [200,201,404] #为了跳过heath step加入404,正常heathlcheck应该2xx的状态码
        except RuntimeError:
            return False

validator.py

from typing import Any, Dict, List
from deepdiff import DeepDiff
import logging
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger("ResponseValidator")
class ResponseValidator:
    @staticmethod
    def validate(
        expected: Dict[str, Any],
        actual: Dict[str, Any]
    ) -> List[str]:
        """深度验证数据结构差异"""
        logger.debug(f"expected: {expected}")
        logger.debug(f"actual: {actual}")
        diff = DeepDiff(
            expected,
            actual,
            ignore_order=True,
            exclude_paths=["root['timestamp']"]
        )
        if not diff:
            return []
        diff_dict = diff.to_dict()
        # 将每个差异项转换成字符串
        diff_str_list = []
        for path, change in diff_dict.items():
            for key, value in change.items():
                diff_str_list.append(f"{key}: {value}")
        logger.debug(f"Differences found: {diff_str_list}")
        return diff_str_list
        
        #return list(diff.to_dict().values()) if diff else []

    @staticmethod
    def status_code_validate(expected: int, actual: int) -> str:
        """状态码验证"""
        if expected != actual:
            msg = f"Expected status {expected}, got {actual}"
            logger.error(msg)
            return f"Expected status {expected}, got {actual}"
        logger.debug("Status codes match.")
        return ""

 testconfig.yaml

testsuit_name: server_test
Front: catfact.ninja/fact/1
back_end: catfact.ninja/fact/1
testcase:
  - Test_0001
  - Test_0002

 Test_0001.yaml
 

description: "check"
request:
  method: GET
  path: "/"
  headers:
    Content-Type: application/json
  body:
    product_id: "A123"
    quantity: 2
expected:
  status_code: 404
  data:
    message: 'Not Found'
    code: 404

测试结果

python -m ftfw.cli server_test
2025-05-04 03:55:10,929 - root - DEBUG - Received testsuites: ['server_test']
2025-05-04 03:55:10,929 - root - INFO - Begin run testsuite: ft\server_test
2025-05-04 03:55:10,929 - myhttp - DEBUG - Sending GET request to http://catfact.ninja/fact/1 with headers=None and body=None
2025-05-04 03:55:10,947 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): catfact.ninja:80
2025-05-04 03:55:11,630 - urllib3.connectionpool - DEBUG - http://catfact.ninja:80 "GET /fact/1 HTTP/11" 301 None
2025-05-04 03:55:11,637 - urllib3.connectionpool - DEBUG - Starting new HTTPS connection (1): catfact.ninja:443
2025-05-04 03:55:12,240 - urllib3.connectionpool - DEBUG - https://catfact.ninja:443 "GET /fact/1 HTTP/11" 404 None
2025-05-04 03:55:12,252 - executor - INFO - ✅ Front service healthy
2025-05-04 03:55:12,252 - myhttp - DEBUG - Sending GET request to http://catfact.ninja/fact/1 with headers=None and body=None
2025-05-04 03:55:12,487 - urllib3.connectionpool - DEBUG - http://catfact.ninja:80 "GET /fact/1 HTTP/11" 301 None
2025-05-04 03:55:12,712 - urllib3.connectionpool - DEBUG - https://catfact.ninja:443 "GET /fact/1 HTTP/11" 404 None
2025-05-04 03:55:12,712 - executor - INFO - ✅ Backend service healthy
2025-05-04 03:55:12,712 - root - DEBUG - Execute testcase Test_0001 in suite server_test
2025-05-04 03:55:12,737 - executor - DEBUG - case description: check
2025-05-04 03:55:12,737 - myhttp - DEBUG - Sending GET request to https://catfact.ninja/fact/1/ with headers={'Content-Type': 'application/json'} and body={'product_id': 'A123', 'quantity': 2}
2025-05-04 03:55:12,966 - urllib3.connectionpool - DEBUG - https://catfact.ninja:443 "GET /fact/1/ HTTP/11" 404 None
2025-05-04 03:55:12,966 - executor - DEBUG - get response as below code: 404,data:b'{"message":"Not Found","code":404}'
2025-05-04 03:55:12,967 - ResponseValidator - DEBUG - Status codes match.
2025-05-04 03:55:12,968 - ResponseValidator - DEBUG - expected: {'message': 'Not Found', 'code': 404}     
2025-05-04 03:55:12,968 - ResponseValidator - DEBUG - actual: {'message': 'Not Found', 'code': 404}       
2025-05-04 03:55:12,968 - report - DEBUG - ▶ Test_0001: PASS
2025-05-04 03:55:12,968 - root - DEBUG - Execute testcase Test_0002 in suite server_test
2025-05-04 03:55:12,975 - executor - DEBUG - case description: check2
2025-05-04 03:55:12,976 - myhttp - DEBUG - Sending GET request to https://catfact.ninja/fact/1/ with headers={'Content-Type': 'application/json'} and body={'product_id': 'A123', 'quantity': 2}
2025-05-04 03:55:13,226 - urllib3.connectionpool - DEBUG - https://catfact.ninja:443 "GET /fact/1/ HTTP/11" 404 None
2025-05-04 03:55:13,226 - executor - DEBUG - get response as below code: 404,data:b'{"message":"Not Found","code":404}'
2025-05-04 03:55:13,227 - ResponseValidator - DEBUG - Status codes match.
2025-05-04 03:55:13,227 - ResponseValidator - DEBUG - expected: {'length': 110}
2025-05-04 03:55:13,227 - ResponseValidator - DEBUG - actual: {'message': 'Not Found', 'code': 404}       
2025-05-04 03:55:13,228 - ResponseValidator - DEBUG - Differences found: ["root: {'new_value': {'message': 'Not Found', 'code': 404}, 'old_value': {'length': 110}}"]
2025-05-04 03:55:13,228 - report - DEBUG - ▶ Test_0002: FAILLED
2025-05-04 03:55:13,228 - report - DEBUG -   ✖ Issues:
2025-05-04 03:55:13,228 - report - DEBUG -     - root: {'new_value': {'message': 'Not Found', 'code': 404}, 'old_value': {'length': 110}}
2025-05-04 03:55:13,229 - report - DEBUG - Test suit result:
2025-05-04 03:55:13,229 - report - DEBUG - 🕒 Start Time: 2025-05-04 03:55:10
2025-05-04 03:55:13,229 - report - DEBUG - ⏱️ Duration: 2.30s
2025-05-04 03:55:13,230 - report - DEBUG - ✅ Passed: 1
2025-05-04 03:55:13,230 - report - DEBUG - ❌ Failed: 1

2025-05-04 03:55:13,230 - report - DEBUG - Test suit FAILED cases list:
▸ server_test/Test_0002 FAILED

注解:

如果python -m ftfw.cli后面不写测试文件夹,或过滤当前目下所有问价夹进行测试,在heathl check的那一步 resp.status_code in  [200,201,404] #为了跳过heath step加入404,正常heathlcheck应该2xx的状态码。

重要的第三方库

1. PyYAML

用途:解析 YAML 格式的配置文件(如 testconfig.yaml)和测试用例文件。测试套件配置加载:
在 TestRunner 类的 _load_config 方法中,用于加载 testconfig.yaml以及用例解析:

import yaml
with open(config_file) as f:
    config = yaml.safe_load(f)

2. requests

用途:发送 HTTP 请求到被测试的微服务(如 client 和 server)。服务健康检查:在 HttpClient 类的 check_service_health 方法中,发送探活请求:resp = self.send_request("GET", f"{endpoint}/health");测试请求发送

3. deepdiff

用途:深度比较预期结果与实际响应的差异,支持复杂数据结构(如嵌套 JSON)。

4. tenacity

用途:为 HTTP 请求添加重试机制,提升网络波动的容错性。HTTP 请求重试。

5. coloredlogs

用途:美化控制台日志输出,提升可读性(如不同颜色区分日志级别)。coloredlogs.install(level=logging.INFO)

6. colored

用途:在终端输出彩色文本,突出显示测试结果(如红色标记失败用例)。

from colored import fg, attr
        print(f"{fg('green')}✅ PASSED{attr('reset')}")
        print(f"{fg('red')}❌ FAILED{attr('reset')}")

依赖汇总 (requirements.txt)
 

PyYAML==6.0.1       # YAML解析
requests==2.31.0    # HTTP请求
deepdiff==6.7.1     # 数据对比
tenacity==8.2.3     # 重试机制
coloredlogs==15.0.1 # 日志美化
colored==1.4.4      # 终端颜色

基于Selenium+Python的web自动化测试框架(附框架源码+项目实战) Selenium是一个基于浏览器的自动化测试工具,它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分:Selenium IDE、Selenium WebDriver 和Selenium Grid。Selenium IDE:Firefox的一个扩展,它可以进行录制回放,并把录制的操作以多种语言(例如java、python等)的形式导出成测试用例。Selenium WebDriver:提供Web自动化所需的API,主要用作浏览器控制、页面元素选择和调试。 阅读详情

相关推荐

Selenium自动化测试框架搭建:四层架构与数据驱动实战指南

自动化测试是提升软件交付效率与质量的关键技术,其核心原理在于通过脚本模拟用户操作,实现测试流程的自动化执行。在Web UI自动化领域,Selenium作为广泛使用的底层库,提供了强大的浏览器操控能力。然而,直接使用Selenium编写脚本往往面临维护性差、稳定性低等挑战,其技术价值在于通过构建标准化的测试框架,将松散的操作封装为可复用、易维护的工程组件。这通常需要结合模块化设计、数据驱动等工程实践,以应对复杂的应用场景,如持续集成、多环境测试等。本文聚焦于基于Selenium、Pytest和Page Obj

weixin_27918373的博客 331

软件测试 —— 常见的自动化测试架构!

一个自动化测试架构就是一个集成体系,其中定义了一个特殊软件产品的自动化测试规则。这一体系中包含测试功能函数库、测试数据源、测试对象识别标准,以及各种可重用的模块。这些组件作为小的构建模块,被组合起来代表某种商业流程。自动化测试架构提供了自动化测试的基础,降低了自动化测试的难度。

m0_58026506的博客 558

Java自动化测试框架搭建:从JUnit 5、RestAssured到分层架构实战

自动化测试是软件工程中保障质量、提升交付效率的核心实践。其原理在于通过脚本模拟用户操作或系统交互,自动执行预设检查点,替代重复人工验证。这一技术能显著降低回归成本,快速反馈缺陷,是现代DevOps和持续集成/持续部署(CI/CD)流程的关键环节。在Java技术栈中,JUnit 5作为标准的单元测试框架,与RestAssured这一API测试利器结合,构成了构建健壮测试框架的坚实基础。通过分层架构设计(如驱动层、页面对象层、业务逻辑层和测试用例层),可以实现测试代码的高内聚、低耦合,从而应对复杂的业务场景与频

weixin_28839629的博客 226

这些自动化测试框架知识你还不知道?

这些自动化测试框架知识你还不知道?!   无论是在自动化测试实践,还是日常交流中,经常听到一个词:框架。之前学习自动化测试的过程中,一直对“框架”这个词知其然不知其所以然。   最近看了很多自动化相关的资料,加上自己的一些实践,算是对“框架”有了一些理解,这篇博客,就聊聊自动化框架的一些事吧。 一、什么是框架 框架(framework)是一个框子——指其约束性,...

ak52152111的博客 729

UI自动化测试框架搭建:从分层架构到工程化实践

UI自动化测试是软件测试领域保障功能稳定性的关键技术,其核心在于通过工程化手段提升脚本的可维护性和执行效率。其原理基于分层架构设计,将测试数据、业务逻辑与页面元素分离,从而构建出高复用、低耦合的测试体系。这一技术价值在于能够显著降低回归测试成本,并提升测试覆盖的稳定性。在实际应用场景中,结合Page Object Model设计模式与数据驱动测试,可以高效应对Web、桌面及移动端应用的自动化需求。本文以Selenium和Playwright等主流工具为例,深入探讨了框架搭建中的分层架构、驱动管理、测试数据管

weixin_33670713的博客 335

Robot Framework自动化测试实战:从环境搭建到项目架构设计

自动化测试是现代软件工程中提升交付质量和效率的关键实践,其核心原理是通过脚本模拟用户操作,实现测试流程的自动执行与验证。在众多技术方案中,基于关键字驱动的框架因其低代码、高可读性而备受青睐,尤其适合团队协作与长期维护。Robot Framework作为该领域的代表,通过丰富的库生态和Python扩展能力,为Web UI、API、移动端等多场景测试提供了统一解决方案。本文聚焦于Robot Framework的实战应用,详细解析其环境配置、SeleniumLibrary等核心库的使用技巧,并深入探讨如何设计可维

weixin_26833139的博客 317

什么是自动化测试框架?如何搭建自动化测试框架详解

无论是在自动化测试实践,还是日常交流中,经常听到一个词:框架。之前学习自动化测试的过程中,一直对“框架”这个词知其然不知其所以然。最近看了很多自动化相关的资料,加上自己的一些实践,算是对“框架”有了一些理解,这篇博客,就聊聊自动化框架的一些事吧。。。

HUA6911的博客 939

DDPG算法实战:用Python一步步实现深度强化学习中的连续动作控制

本文详细介绍了DDPG算法在深度强化学习中的连续动作控制应用,通过Python和PyTorch实现从零构建智能体。内容涵盖环境搭建、网络架构设计、经验回放、OU噪声实现及训练流程优化,帮助开发者掌握DDPG在机械臂控制等场景的核心技术。

weixin_30335575的博客 416

简易自动化测试架构搭建

简易自动化测试架构搭建前言硬件准备软件准备操作系统PythonSTF代码编写一些踩过的坑 前言 前一段时间,用python+夜神实现了基于模拟器的自动化测试。链接见:https://blog.youkuaiyun.com/saint_228/article/details/88351048。最近想把自动化在真机上实现。所以捣鼓了几天,弄了个简易版测试架构,开销不多,效果不错,在此分享给大家。 硬件准备 我们公...

saint_228的博客 2205

《计算机网络》复试问答题总结

DNS 是一个联机分布式数据库系统,负责主机名和 IP 地址之间的转换,需要进行域名查询的机器主动发起域名解析请求,域名服务器则随时准备做出响应。域名服务器的数据库中存放着它所管辖范围的主机名和 IP 地址之间的映射表,域名服务器之间又可以相互联络和协作,以便分布在 Internet 各个域名服务器数据库中的域名都能被有效地搜索,从而实现主机名与 IP 地址的映射。为了提高解析效率,使用了域名缓存技术。在服务器、主机中设置一个专用的内存缓冲区。服务器用来存放近期解析过的域名及其对应的 IP 地址的映射。

weixin_45971154的博客 2029

从零开始:使用COLMAP与OpenMVS构建高精度三维Mesh模型

本文为零基础新手提供了使用COLMAP与OpenMVS构建高精度三维Mesh模型的完整实战指南。详细介绍了从环境配置、数据采集黄金法则,到COLMAP稀疏与稠密重建、OpenMVS网格生成与纹理映射的每一步操作,并分享了关键的避坑技巧与参数调优建议,帮助读者从二维照片中成功重建出属于自己的三维模型。

metal的博客 505

从0开始搭建自动化测试框架之PO分层架构

全称:page object model 简称:POM/PO。

2401_86343726的博客 876

告别电压瓶颈:手把手教你用SVPWM过调制算法提升电机驱动性能(附Simulink仿真)

本文详细介绍了如何利用SVPWM过调制算法提升电机驱动性能,解决电动汽车高速运行时的电压瓶颈问题。通过过调制I区和II区的算法实现细节,包括相角补偿与跳变控制,结合Simulink仿真演示,帮助工程师和研究者平衡电压增益与谐波失真,提升系统效率与动态响应。

weixin_30315723的博客 392

正式搭建自动化测试平台Snail_Autotest_Platfrom(1)——整体架构设计

文章目录 前面14篇文章记录了我从小白初步认识学习Django的一些基础知识,这里开始正式着手搭建自动化测试平台,欢迎大家关注公众号【学python的蜗牛】!

十七光年的博客 2206

图像去畸变(相机成像、畸变模型)

首先通过上一步相机标定得到畸变参数和相机内参,同时,由于图像去畸变都是在归一化相机坐标系下进行的,而图像都是像素坐标,所以需要先通过相机内参矩阵将图片上的像素点从像素坐标系转变到归一化坐标系,再通过畸变参数在归一化坐标上进行去畸变的操作。由于畸变是发生在世界点通过透镜投影到相机坐标系这个过程中的,所以去畸变操作是在相机坐标系下进行的。(如图,Pd即为畸变后成像点)3.读取图像和各个参数,调用去畸变函数(opencv自带的cv.undistort函数),就可以完成对图像的去畸变处理,进而得到去畸变的图像。

lm5688jjupm1n28的博客 1051

Qt6导入QtChart绘制图表(添加组件)

我在按照大佬们的说明添加QtChart时发现,我的电脑上并不能正确的添加QtChart并编译通过,所以在综合了两位大佬的说明又自己探索了一番后,正确编译通过,分享出来,以便同志们节省一些时间。

Nuaa0719301zyx的博客 2951

Python自动化测试框架搭建教程:从架构到实现

本文将介绍如何使用Python构建一个自动化测试框架,覆盖到了从架构设计到示例代码的全过程。

公众号【伤心的辣条】资料领取~ 417

手把手教你用CODESYS将旧笔记本改造成软PLC(含证书配置避坑指南)

本文提供了一份详尽的指南,教你如何利用CODESYS平台将闲置旧笔记本改造为一台功能完整的软PLC。内容涵盖从硬件评估、系统实时性优化、软件安装与驱动配置,到关键的证书授权管理避坑指南,旨在帮助自动化工程师和爱好者低成本搭建稳定可靠的学习与开发平台。

weixin_29190573的博客 279

从零搭建企业级API自动化测试平台:架构设计与工程实践

API自动化测试是现代软件工程中保障接口质量、提升研发效能的核心实践。其基本原理是通过脚本或平台模拟客户端请求,对服务端接口进行自动化验证,替代传统低效的人工测试。在技术价值层面,它不仅能显著提升测试覆盖率和执行效率,降低回归成本,更是实现持续集成与持续交付的关键环节。典型的应用场景包括日常开发联调、版本发布回归、以及CI/CD流水线中的质量门禁。本文聚焦于如何从零设计并实现一个贴合团队需求的企业级API自动化测试平台,深入探讨了以pytest和Celery为核心的技术选型、测试用例的动态生成与异步执行机制

weixin_27791839的博客 311

vue2 - 详细实现播放AMR格式音频并实现自定义播放器样式,支持amr音频文件在线地址URL播放、调用服务端API接口获取二进制文件流amr形式、用户本地上传amr文件并实现立即播放(详细代码)

vue2,amr,amr文件播放,vue播放amr音频文件以及在线url跨域问题解决,vue使用audio标签播放amr音频,amr跨域解决,amr网页解码播放,文件上传后怎么播放,vue2播放.amr语音文件,api接口形式调用获取的amr如何播放,vue中如何使用audio元素播放AMR格式的文件,进度条,vue前端如何播放AMR格式的音频文件,vue播放语音文件AMR格式,播放amr格式音频AMR录音机,h5原生audio播放amr格式的音频,VUE2使用benz-amr-recorder 实现解码、

✅ 精选推荐文章 7414

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Python自动化测试框架实战:从零搭建分层架构与CI/CD集成

自动化测试框架是软件工程中提升测试效率与质量的核心基础设施,其本质是一套标准化的工程解决方案。其核心原理在于通过分层架构(如用例层、逻辑层、驱动层、支撑层)实现关注点分离,并遵循“约定大于配置”的原则。这种设计的技术价值在于大幅降低脚本维护成本、提升团队协作效率,并能无缝集成到CI/CD流水线中,实现质量守护的自动化。在应用场景上,它广泛适用于Web UI、API接口、移动端等多种测试类型。本文以Python技术栈为例,深入探讨如何基于pytest、Selenium、Allure等成熟工具链,从零构建一个具

weixin_28839439的博客 374

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Pytest接口自动化测试框架搭建:从架构设计到工程实践

接口自动化测试是现代软件工程中保障软件质量、提升测试效率的关键实践。其核心原理是通过脚本模拟客户端请求,对服务端接口进行功能、性能及稳定性的自动化验证,从而替代大量重复的手工测试。在技术选型上,Python生态中的Pytest框架因其简洁的语法、强大的Fixture机制和丰富的插件生态,成为构建健壮测试套件的首选。结合Requests库处理HTTP请求、PyYAML管理测试数据,可以高效实现数据与逻辑的分离。这种技术组合的价值在于能够快速构建标准化、可维护的测试工程,统一团队协作规范,并内置日志、报告等最佳

weixin_28829325的博客 321

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

上一篇: python项目实战-后端个人博客系统
下一篇: 机器学习-线性模型
刀客123
博客等级 码龄11年 800粉丝 120原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值