求助:如何通过pytest向init.py传入mock boto3 client?调用真实实例问题
问题原因
你的核心问题出在模块导入时机与mock生效时机不匹配:
init.py中的params = {'token': get_token()}、job_1()、job_2(params)属于模块级代码,当你在测试中(或pytest执行时)导入init模块时,这些代码会立刻执行,而此时你的mock_boto3_clientfixture还未被激活(fixture在测试函数运行阶段才会生效)。- 另外,你测试用例中创建的secret名称是
my-test-secret,但实际代码调用的是my-secret/my-token,即使mock生效也会找不到对应secret。
解决方案
要解决这个问题,需要从两个方面调整:
- 重构
init.py,避免模块级别的立即执行代码,将业务逻辑封装到函数中,由外部控制执行时机。 - 确保mock在代码执行前就生效,同时修正secret名称不匹配的问题。
步骤1:重构init.py
把模块级执行的代码放到函数里,只有当脚本直接运行时才执行:
from helpers.token_helper import get_token from my_jobs.jobs import job_1, job_2 def run_jobs(): params = {'token': get_token()} job_1() job_2(params) # 只有直接运行该脚本时才执行,导入时不触发 if __name__ == "__main__": run_jobs()
步骤2:修改测试用例
使用autouse=True的fixture提前激活moto mock,确保在任何代码执行前mock就生效,同时修正secret名称与代码保持一致:
import pytest import boto3 import moto from my_jobs.init import run_jobs from my_jobs.jobs import job_1 @pytest.fixture(scope="session", autouse=True) def mock_secretsmanager(): with moto.mock_secretsmanager(): client = boto3.client("secretsmanager", region_name="us-east-1") # 与代码中调用的secret名称保持一致 secret_name = "my-secret/my-token" secret_value = "my-test-secret-value" client.create_secret(Name=secret_name, SecretString=secret_value) yield client def test_job_1(): response = job_1() assert response == 'yay' def test_run_jobs(mock_secretsmanager): # 此时调用run_jobs,get_token会使用mock的secretsmanager run_jobs() # 可根据job_2的实际行为添加断言,比如验证参数是否正确传递
可选优化:让aws_helper更易测试
可以修改aws_helper.py,允许传入自定义的boto3客户端,这样测试时可以直接注入mock客户端,无需依赖moto:
import boto3 def get_secret(secret_name, client=None): # 如果没有传入client,才创建默认的 if not client: client = boto3.client("secretsmanager", region_name="us-east-1") try: response = client.get_secret_value(SecretId=secret_name) return response["SecretString"] except Exception as e: print(e) raise e
这样在测试token_helper.py时,可以直接传入mock客户端:
from unittest.mock import Mock from helpers.token_helper import get_token def test_get_token_with_secret(): mock_client = Mock() mock_client.get_secret_value.return_value = {"SecretString": "test-token"} from helpers import token_helper # 替换token_helper里的get_secret为带mock客户端的版本 def mock_get_secret(name): return token_helper.get_secret(name, client=mock_client) token_helper.get_secret = mock_get_secret token = get_token() assert token == "test-token"
内容的提问来源于stack exchange,提问作者walkingwithataco
相关产品推荐
相关产品推荐

