You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让pytest触发预期异常?Key Vault环境变量测试失败

问题:无法测试KEY_VAULT_NAME环境变量不存在的场景

我需要验证当KEY_VAULT_NAME环境变量未设置时,代码会抛出指定的ValueError,对应核心代码片段:

if not self.key_vault_name:
    raise ValueError("Key Vault name is not set in the environment variables.")

SecretHandler类实现

from azure.identity import ClientSecretCredential
from azure.keyvault.secrets import SecretClient
from dotenv import load_dotenv
import os


class SecretHandler:
    """
    Handles the retrieval of secrets from an Azure Key Vault.

    This class uses Azure's ClientSecretCredential for authentication and
    SecretClient for accessing the secrets stored in the Azure Key Vault.
    The credentials and Key Vault name are loaded from environment variables.
    """

    def __init__(self, dotenv_file: str = "src\\config\\.env") -> None:
        """
        Initializes the SecretHandler instance by loading environment variables
        and setting up the credential for Azure Key Vault access using client ID,
        tenant ID, and client secret.
        """
        load_dotenv(dotenv_path=dotenv_file)

        self.key_vault_name = os.getenv("KEY_VAULT_NAME")
        if not self.key_vault_name:
            raise ValueError("Key Vault name is not set in the environment variables.")

        try:
            self.credential = ClientSecretCredential(
                client_id=os.getenv("AZURE_CLIENT_ID"),
                tenant_id=os.getenv("AZURE_TENANT_ID"),
                client_secret=os.getenv("AZURE_CLIENT_SECRET"),
            )
        except Exception as e:
            raise ValueError(f"Failed to obtain credential: {e}")

        self.secrets = {}

    def retrieve_secrets(self) -> None:
        """
        Retrieves all secrets from the Azure Key Vault.

        This method uses the SecretClient to list all the secret properties
        in the Key Vault and then retrieves the actual secret values, storing
        them in the `self.secrets` dictionary.
        """
        secret_client = SecretClient(
            vault_url=f"https://{self.key_vault_name}.vault.azure.net/",
            credential=self.credential,
        )

        try:
            secret_properties = secret_client.list_properties_of_secrets()
            for secret_property in secret_properties:
                secret = secret_client.get_secret(secret_property.name)
                self.secrets[secret.name] = secret.value
        except Exception as e:
            raise ValueError(f"Failed to retrieve secrets: {e}")

测试用例代码

@patch("src.config.secret_handler.ClientSecretCredential", autospec=True)
def test_init_secret_handler_wrong_key_vault(mock_client_secret_credential):
    # Mock setup for successful credential initialization
    client_secret_credential_instance = Mock()
    mock_client_secret_credential.return_value = client_secret_credential_instance

    # Mock environment without KEY_VAULT_NAME
    with patch.dict('os.environ', {'AZURE_CLIENT_ID': 'test_client_id',
                                   'AZURE_TENANT_ID': 'test_tenant_id',
                                   'AZURE_CLIENT_SECRET': 'test_client_secret'}):
        
        # Test initialization expecting ValueError from SecretHandler
        with pytest.raises(ValueError, match="Key Vault name is not set in the environment variables."):
            SecretHandler(dotenv_file="test/config/failed_env.env")

运行错误

FAILED test/config/test_secret_handler.py::test_init_secret_handler_wrong_key_vault - Failed: DID NOT RAISE <class 'ValueError'>

解决建议

1. 禁用load_dotenv加载测试文件

测试中load_dotenv会读取指定的failed_env.env文件,如果该文件中存在KEY_VAULT_NAME(哪怕是空值),会覆盖mock的环境变量,导致异常不触发。直接mockload_dotenv避免加载外部文件,让环境变量完全由测试控制:

@patch("src.config.secret_handler.ClientSecretCredential", autospec=True)
@patch("src.config.secret_handler.load_dotenv")  # 禁用env文件加载
def test_init_secret_handler_wrong_key_vault(mock_load_dotenv, mock_client_secret_credential):
    client_secret_credential_instance = Mock()
    mock_client_secret_credential.return_value = client_secret_credential_instance

    with patch.dict('os.environ', {
        'AZURE_CLIENT_ID': 'test_client_id',
        'AZURE_TENANT_ID': 'test_tenant_id',
        'AZURE_CLIENT_SECRET': 'test_client_secret'
        # 不包含KEY_VAULT_NAME
    }):
        with pytest.raises(ValueError, match="Key Vault name is not set in the environment variables."):
            SecretHandler(dotenv_file="test/config/failed_env.env")

2. 确保测试用env文件无KEY_VAULT_NAME

如果必须保留load_dotenv加载文件的逻辑,检查test/config/failed_env.env文件,确保其中完全不存在KEY_VAULT_NAME配置项,或者将其设置为空值(KEY_VAULT_NAME=),这样os.getenv返回空字符串,触发if not self.key_vault_name判断。

3. 强制mockKEY_VAULT_NAME为空

在patch.dict中明确设置KEY_VAULT_NAME为空字符串,确保无论env文件是否有配置,都会触发异常:

with patch.dict('os.environ', {
    'AZURE_CLIENT_ID': 'test_client_id',
    'AZURE_TENANT_ID': 'test_tenant_id',
    'AZURE_CLIENT_SECRET': 'test_client_secret',
    'KEY_VAULT_NAME': ''
}):

内容的提问来源于stack exchange,提问作者fabriziocucina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 19:39:51