Python测试开发工具库:测试环境变量统一配置与加载工具

目录

一、为什么需要环境变量统一配置工具?

二、核心原理:配置加载的底层逻辑

三、设计思路:如何打造一个好用的配置加载工具?

[3.1 配置文件格式选型](#3.1 配置文件格式选型)

[3.2 多环境支持](#3.2 多环境支持)

[3.3 环境切换方式](#3.3 环境切换方式)

[3.4 配置加载与调用](#3.4 配置加载与调用)

[3.5 敏感信息处理](#3.5 敏感信息处理)

[3.6 异常处理](#3.6 异常处理)

四、代码实现:手把手教你写工具

[4.1 代码目录结构](#4.1 代码目录结构)

[4.2 依赖包说明](#4.2 依赖包说明)

[4.3 配置文件编写](#4.3 配置文件编写)

[4.3.1 base.yaml](#4.3.1 base.yaml)

[4.3.2 env.yaml](#4.3.2 env.yaml)

[4.4 敏感信息加密解密工具(encrypt_utils.py)](#4.4 敏感信息加密解密工具(encrypt_utils.py))

[4.5 核心配置加载工具(config_loader.py)](#4.5 核心配置加载工具(config_loader.py))

[4.6 工具使用示例(test_demo.py)](#4.6 工具使用示例(test_demo.py))

五、应用场景:工具在实际工作中的用法

[5.1 接口自动化测试](#5.1 接口自动化测试)

[5.2 自动化流水线集成](#5.2 自动化流水线集成)

[5.3 多环境数据对比测试](#5.3 多环境数据对比测试)

[5.4 敏感信息隔离](#5.4 敏感信息隔离)

六、注意事项


在测试开发工作中,环境变量管理是高频痛点之一。本地开发、测试、预发等多环境下,数据库地址、接口基础路径(baseUrl)、第三方服务密钥等配置信息存在差异,传统方式需手动修改脚本中的变量以切换环境;更存在团队协作时,敏感信息被硬编码至脚本并提交版本库,进而导致信息泄露的风险。

本文将介绍一款测试环境变量统一配置与加载工具,该工具基于Python 3.8.6实现,可实现环境变量的规范化管理、高效切换,同时规避敏感信息泄露风险,是测试开发工作中的实用支撑工具。

一、为什么需要环境变量统一配置工具?

在测试开发工作中,环境变量的管理是绕不开的环节,比如接口测试的baseUrl、数据库连接参数(地址、端口、用户名、密码)、第三方服务的密钥(如短信接口密钥、支付接口密钥)、文件存储路径等,这些变量在不同环境下取值不同。

如果没有统一的配置管理方案,通常会遇到这些问题:

  • 硬编码冗余:把环境变量直接写在代码里,不同环境的脚本需要单独维护,重复劳动多;

  • 切换效率低:切换环境时要手动修改多处代码中的变量,容易遗漏或出错;

  • 敏感信息泄露:硬编码的密钥、密码等信息可能被提交到版本库,造成安全风险;

  • 协作成本高:团队成员之间的环境配置不统一,导致脚本在不同人本地运行结果不一致。

而环境变量统一配置与加载工具,就是为了解决这些问题而生的,核心价值在于"统一管理、灵活切换、安全隔离"。

二、核心原理:配置加载的底层逻辑

这个工具的核心原理其实很简单:将不同环境的变量统一存储在配置文件中,通过指定环境类型,让工具自动读取对应环境的配置,加载到程序中供后续使用。

整个流程可以分为三个步骤:

  1. 配置文件定义:按照固定格式(如YAML,简洁易读、支持分层结构)编写配置文件,区分不同环境(如dev、test、prod)的变量;

  2. 环境指定:通过命令行参数、系统环境变量或配置文件指定当前需要使用的环境;

  3. 配置加载:工具读取配置文件,根据指定的环境筛选对应的变量,加载到内存中(如字典),供脚本调用。

三、设计思路:如何打造一个好用的配置加载工具?

基于核心原理,结合测试开发的实际使用场景,我把工具的设计思路拆解为以下几个关键点,确保工具的易用性和扩展性:

3.1 配置文件格式选型

可选的配置文件格式有JSON、INI、YAML等。JSON格式严谨但写起来繁琐;INI格式适合简单配置,不支持复杂的分层结构;YAML格式简洁易读,支持分层嵌套,能很好地满足多环境、多类型变量的配置需求,所以最终选择YAML作为配置文件格式。

3.2 多环境支持

配置文件中需要区分不同环境,比如开发环境(dev)、测试环境(test)、预发环境(staging)等。采用"顶层环境分组+内层变量"的结构,每个环境下的变量可以灵活定义,支持不同环境下变量数量和类型的差异。

3.3 环境切换方式

为了适配不同的使用场景,设计两种环境切换方式:

  • 命令行参数指定:运行脚本时通过参数(如--env test)指定环境,灵活方便;

  • 系统环境变量指定:在系统中设置环境变量(如TEST_ENV=test),适合批量运行脚本或自动化流水线场景。

同时设置优先级:命令行参数优先级高于系统环境变量,当两种方式都指定时,以命令行参数为准;如果都未指定,默认使用开发环境(dev)。

3.4 配置加载与调用

工具加载配置后,提供简洁的调用方式,比如通过"配置对象.环境.变量名"或"配置字典['环境']['变量名']"的方式获取变量值。同时支持配置参数的默认值填充,当某个环境缺少某个变量时,使用默认值,避免脚本运行报错。

3.5 敏感信息处理

对于密码、密钥等敏感信息,不直接明文存储在配置文件中。设计敏感信息加密存储功能,工具加载时自动解密,确保敏感信息的安全性。

3.6 异常处理

考虑各种异常场景,比如配置文件不存在、指定的环境不存在、配置文件格式错误、敏感信息解密失败等,添加对应的异常捕获和提示,帮助用户快速定位问题。

四、代码实现:手把手教你写工具

接下来进入核心的代码实现部分。首先给出工具的整体代码目录结构,然后分模块讲解实现细节。

4.1 代码目录结构

复制代码
python 复制代码
env_config_tool/
├── config/
│   ├── base.yaml          # 基础配置(默认值、通用配置)
│   └── env.yaml           # 多环境变量配置
├── utils/
│   ├── config_loader.py   # 配置加载核心工具
│   └── encrypt_utils.py   # 敏感信息加密解密工具
└── test_demo.py           # 工具使用示例脚本

4.2 依赖包说明

工具基于Python 3.8.6开发,需要依赖以下第三方包:

  • PyYAML:用于解析YAML配置文件;

  • argparse:用于解析命令行参数(Python标准库,无需额外安装);

  • cryptography:用于敏感信息的加密和解密。

安装命令:

pip install pyyaml cryptography==3.4.7 # 指定cryptography版本适配Python3.8.6

4.3 配置文件编写

首先编写两个配置文件:base.yaml(基础配置,存储默认值和通用配置)和env.yaml(多环境变量配置)。

4.3.1 base.yaml
复制代码
python 复制代码
# 基础配置:通用配置和默认值
common:
  timeout: 30
  retry_count: 2
default_env: dev  # 默认环境
4.3.2 env.yaml
复制代码
python 复制代码
# 多环境变量配置
dev:  # 开发环境
  base_url: "http://dev-api.example.com"
  db:
    host: "127.0.0.1"
    port: 3306
    user: "root"
    password: "dev123456"  # 开发环境密码,可明文(仅示例,实际建议加密)
  redis:
    host: "127.0.0.1"
    port: 6379

test:  # 测试环境
  base_url: "http://test-api.example.com"
  db:
    host: "192.168.1.100"
    port: 3306
    user: "test_user"
    password: "Encrypted:xxxxxx"  # 加密后的密码
  redis:
    host: "192.168.1.101"
    port: 6379

staging:  # 预发环境
  base_url: "http://staging-api.example.com"
  db:
    host: "10.0.0.100"
    port: 3306
    user: "staging_user"
    password: "Encrypted:yyyyyy"
  redis:
    host: "10.0.0.101"
    port: 6379

4.4 敏感信息加密解密工具(encrypt_utils.py)

使用cryptography库实现AES加密和解密,用于处理敏感信息。这里采用AES-CBC模式,需要注意密钥和偏移量的保管。

复制代码
python 复制代码
from cryptography.fernet import Fernet

# 注意:实际使用时,密钥需要妥善保管,不要硬编码在代码中
# 可以通过系统环境变量或配置文件(单独加密)加载
SECRET_KEY = b'your-secret-key-here-32bytes-long'  # AES-256需要32字节密钥

def encrypt_data(data):
    """
    加密数据
    :param data: 待加密的字符串
    :return: 加密后的字符串
    """
    f = Fernet(SECRET_KEY)
    encrypted_data = f.encrypt(data.encode('utf-8'))
    return encrypted_data.decode('utf-8')

def decrypt_data(encrypted_data):
    """
    解密数据
    :param encrypted_data: 加密后的字符串
    :return: 解密后的原始字符串
    """
    f = Fernet(SECRET_KEY)
    decrypted_data = f.decrypt(encrypted_data.encode('utf-8'))
    return decrypted_data.decode('utf-8')

4.5 核心配置加载工具(config_loader.py)

这是工具的核心模块,负责读取配置文件、解析命令行参数、加载指定环境的配置、处理敏感信息解密等。

复制代码
python 复制代码
import os
import yaml
import argparse
from utils.encrypt_utils import decrypt_data

class ConfigLoader:
    def __init__(self):
        # 初始化配置文件路径
        self.base_config_path = os.path.join(os.path.dirname(__file__), '../config/base.yaml')
        self.env_config_path = os.path.join(os.path.dirname(__file__), '../config/env.yaml')
        # 加载基础配置
        self.base_config = self._load_yaml_file(self.base_config_path)
        # 加载多环境配置
        self.env_config = self._load_yaml_file(self.env_config_path)
        # 获取当前环境(命令行参数 > 系统环境变量 > 基础配置默认值)
        self.current_env = self._get_current_env()
        # 加载当前环境的配置
        self.current_config = self._load_current_env_config()

    def _load_yaml_file(self, file_path):
        """
        加载YAML配置文件
        :param file_path: 配置文件路径
        :return: 配置字典
        """
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"配置文件不存在:{file_path}")
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                return yaml.safe_load(f)
        except yaml.YAMLError as e:
            raise Exception(f"配置文件格式错误:{e}")

    def _get_current_env(self):
        """
        获取当前环境
        :return: 当前环境名称(dev/test/staging等)
        """
        # 1. 从命令行参数获取
        parser = argparse.ArgumentParser(description='环境变量配置工具')
        parser.add_argument('--env', help='指定环境(dev/test/staging)')
        args = parser.parse_args()
        if args.env:
            return args.env
        # 2. 从系统环境变量获取
        system_env = os.getenv('TEST_ENV')
        if system_env:
            return system_env
        # 3. 使用基础配置中的默认环境
        return self.base_config.get('default_env', 'dev')

    def _load_current_env_config(self):
        """
        加载当前环境的配置,合并基础配置的通用信息,处理敏感信息解密
        :return: 当前环境的最终配置字典
        """
        # 检查当前环境是否在配置中存在
        if self.current_env not in self.env_config:
            raise Exception(f"指定的环境不存在:{self.current_env},请检查env.yaml配置")
        # 合并基础配置的通用信息和当前环境的配置
        current_config = self.base_config.get('common', {}).copy()
        current_config.update(self.env_config.get(self.current_env, {}))
        # 处理敏感信息解密(以Encrypted:开头的字段)
        self._decrypt_sensitive_data(current_config)
        return current_config

    def _decrypt_sensitive_data(self, config_dict):
        """
        递归解密配置中的敏感信息
        :param config_dict: 待解密的配置字典
        """
        for key, value in config_dict.items():
            if isinstance(value, dict):
                # 递归处理嵌套字典
                self._decrypt_sensitive_data(value)
            elif isinstance(value, str) and value.startswith('Encrypted:'):
                # 解密以Encrypted:开头的字符串
                encrypted_str = value.replace('Encrypted:', '', 1)
                try:
                    config_dict[key] = decrypt_data(encrypted_str)
                except Exception as e:
                    raise Exception(f"敏感信息解密失败,key:{key},错误:{e}")

    def get(self, key, default=None):
        """
        获取配置值(支持点分隔符,如'db.host')
        :param key: 配置键(可带点分隔符)
        :param default: 默认值
        :return: 配置值
        """
        keys = key.split('.')
        value = self.current_config
        for k in keys:
            if isinstance(value, dict) and k in value:
                value = value[k]
            else:
                return default
        return value

# 单例模式,避免重复加载配置
config_loader = ConfigLoader()

4.6 工具使用示例(test_demo.py)

编写一个示例脚本,演示如何使用配置加载工具获取环境变量,用于接口测试和数据库连接场景。

复制代码
python 复制代码
import requests
from utils.config_loader import config_loader

def test_api_request():
    """
    示例:使用配置中的base_url进行接口请求
    """
    base_url = config_loader.get('base_url')
    timeout = config_loader.get('timeout')
    print(f"当前环境:{config_loader.current_env}")
    print(f"接口基础地址:{base_url}")
    print(f"请求超时时间:{timeout}")
    
    # 发送测试请求
    try:
        response = requests.get(f"{base_url}/health", timeout=timeout)
        print(f"接口健康检查结果:{response.status_code}")
    except Exception as e:
        print(f"接口请求失败:{e}")

def test_db_connect():
    """
    示例:使用配置中的数据库信息模拟连接
    """
    db_config = {
        'host': config_loader.get('db.host'),
        'port': config_loader.get('db.port'),
        'user': config_loader.get('db.user'),
        'password': config_loader.get('db.password')
    }
    print("\n数据库配置信息:")
    for key, value in db_config.items():
        print(f"{key}: {value}")
    # 这里可以添加实际的数据库连接代码
    print("模拟数据库连接成功")

if __name__ == "__main__":
    test_api_request()
    test_db_connect()

五、应用场景:工具在实际工作中的用法

这个配置加载工具在测试开发工作中有很多实用场景,这里列举几个高频场景:

5.1 接口自动化测试

在接口自动化测试脚本中,通过工具加载不同环境的base_url、请求超时时间、密钥等变量。比如运行测试环境的用例时,指定--env test,脚本自动使用测试环境的接口地址和配置,无需修改代码。

运行命令示例:

python test_demo.py --env test

5.2 自动化流水线集成

在Jenkins、GitLab CI等自动化流水线中,通过设置系统环境变量TEST_ENV指定环境,流水线运行脚本时自动加载对应环境的配置。比如预发环境的自动化测试任务,在流水线中设置TEST_ENV=staging,脚本即可自动适配预发环境。

5.3 多环境数据对比测试

在需要对比不同环境数据的测试场景中,可通过工具先后加载不同环境的配置,连接不同环境的数据库或接口,获取数据进行对比。比如对比开发环境和测试环境的接口返回数据一致性。

5.4 敏感信息隔离

对于数据库密码、接口密钥等敏感信息,使用加密功能存储在配置文件中,工具加载时自动解密,避免敏感信息明文暴露在代码或配置文件中,降低泄露风险。

六、注意事项

在使用这个工具的过程中,有几个关键点需要注意,避免出现问题:

  • 配置文件路径:确保脚本运行时能正确找到config目录下的配置文件,建议使用绝对路径拼接的方式(工具中已实现),避免因运行目录不同导致配置文件找不到;

  • 密钥保管:加密敏感信息的密钥不要硬编码在代码中,建议通过系统环境变量或单独的加密文件加载,确保密钥安全;

  • 环境校验:在加载配置后,可添加关键变量的校验逻辑(如base_url是否为空、端口是否为整数),提前发现配置错误;

  • 编码格式:配置文件统一使用UTF-8编码,避免因中文注释等导致的解析错误;

  • 版本控制:配置文件(尤其是env.yaml)需要纳入版本控制,但要注意过滤掉包含明文敏感信息的配置(建议加密后再提交)。

相关推荐
red润2 小时前
python win32COM 对象介绍调用Word、WPS 与应用生态
python
Hello.Reader2 小时前
Flink Avro Format Java / PyFlink 读写、Schema 细节与坑点总结
java·python·flink
我是海飞2 小时前
杰理 AC792N WebSocket 客户端例程使用测试教程
c语言·python·单片机·websocket·网络协议·嵌入式·杰理
shy^-^cky2 小时前
Python程序设计完整复习要点(含实例)
python·期末复习
做萤石二次开发的哈哈3 小时前
萤石开放平台 萤石可编程设备 | 设备脚本自定义开发
开发语言·python·萤石云·萤石·萤石开放平台
liu****3 小时前
能源之星案例
人工智能·python·算法·机器学习·能源
程序员三藏3 小时前
软件测试环境搭建及测试过程
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例