【问题标题】:Running pytest in AWS SAM doesn't use env vars in template.yaml在 AWS SAM 中运行 pytest 不使用 template.yaml 中的环境变量
【发布时间】:2021-03-24 00:11:48
【问题描述】:

我正在用 pytest 测试我的 lambda。我的 lambda GetDevicesFunction 使用位于 utils_layer/python 目录中的共享模块 aurora 中的方法连接到数据库。数据库连接的参数来自 template.yaml。当我运行sam build && sam local invoke 时一切正常,但是当我运行我的 pytest 时,环境变量似乎没有从我的模板中提取出来。这是预期的还是我错过了什么?

这是我的项目...

# project structure

├── __init__.py
├── get_devices
│   ├── __init__.py
│   ├── app.py
│   ├── requirements.txt
├── template.yaml
├── tests
│   ├── __init__.py
│   └── unit
│       ├── __init__.py
│       └── test_handler.py
└── utils_layer
    ├── __init__.py
    ├── python
    │   ├── __init__.py
    │   ├── aurora.py
    │   ├── pg8000
# template.yaml

Globals:
  Function:
    Runtime: python3.8
    MemorySize: 256
    Timeout: 60
    Layers:
      - !Ref UtilsLayer
  Environment:
      Variables:
        DATABASE_NAME: !FindInMap [ResourcesName, !Ref MyEnvironment, databaseName]
        DATABASE_HOST: !FindInMap [ResourcesName, !Ref MyEnvironment, databaseHost]
        DATABASE_PORT: !Ref DatabasePort
        DATABASE_USER: !FindInMap [ResourcesName, !Ref MyEnvironment, databaseUser]
        DATABASE_PASSWORD: !FindInMap [ResourcesName, !Ref MyEnvironment, databasePassword]  
        ENVIRONMENT: !Ref MyEnvironment 

Mappings:
  ResourcesName:
    dev:      
      databaseHost: <db_host>
      databaseName: <db_name>
      databaseUser: <db_user>
      databasePassword: <db_password>

Parameters:
  MyEnvironment:
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - staging
      - prod

Resources:
  GetDevicesFunction:
    Type: AWS::Serverless::Function
    Properties:      
      CodeUri: get_devices/
      Handler: app.lambda_handler                                                  
      Events:
        GetDevicesApiEvent:
          Type: Api
          Properties:         
            Path: /devices
            Method: GET   
            
  UtilsLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      Description: Utils layer
      ContentUri: utils_layer/
      CompatibleRuntimes:        
        - python3.8
      RetentionPolicy: Delete 
# aurora.py

import pg8000

db_host = os.environ.get('DATABASE_HOST')
db_port = os.environ.get('DATABASE_PORT')
db_name = os.environ.get('DATABASE_NAME')
db_user = os.environ.get('DATABASE_USER')
db_password = os.environ.get('DATABASE_PASSWORD')

def make_conn():   
    conn = None
    try:                                          
        conn = pg8000.connect(
            database=db_name, 
            user=db_user, 
            password=db_password, 
            host=db_host                
            )  
    except Exception as e:
        print(f'Connection error: {e}')
    return conn
# test_handler.py

import pytest
from get_devices import app

def test_lambda_handler(apigw_event, mocker):        
    ret = app.lambda_handler(apigw_event, "")  
    data = ret['body']['data'] 
    assert ret["statusCode"] == 200    
    assert len(data) > 0 

任何关于如何从我的模板中提取环境变量的建议将不胜感激。

【问题讨论】:

  • 目前无法从您的模板文件中获取它们(您可以提交功能请求/错误)。相反,将您的环境变量放在一个 json 文件中,然后使用 --env-vars PATH 选项。

标签: python aws-lambda pytest aws-sam


【解决方案1】:

我在conftest.py做了这样的事情:

import os
import yaml

# Read contents from template file. put all parameters out into envs
try:
    with open('template.yaml', 'rt') as handle:
        # Add constructor to handle !'s
        def any_constructor(loader, tag_suffix, node):
            if isinstance(node, yaml.MappingNode):
                return loader.construct_mapping(node)
            if isinstance(node, yaml.SequenceNode):
                return loader.construct_sequence(node)
            return loader.construct_scalar(node)
        # Add constructors
        yaml.add_multi_constructor('', any_constructor, Loader=yaml.SafeLoader)

        # Load yaml
        template = yaml.safe_load(handle)

        # Add to env
        for param in template['Parameters'].items():
            key = param[0]
            value = param[1]['Default']
            os.environ[key] = value

except yaml.YAMLError as e:
    raise ValueError(e)

【讨论】:

    【解决方案2】:

    您可以使用与invoke the function locally 相同的 env.json。在这种情况下,我使用一个专门用于测试的名为test.env.json(从.gitignore 中的源代码控制中排除)和环境参数,格式如下:

    {
        "GetDevicesFunction": {
          "DATABASE_NAME": "dbname",
          "DATABASE_HOST": "dbhost",
          "DATABASE_PORT": "5432",
          "DATABASE_USER": "dbuser",
          "DATABASE_PASSWORD": "dbpassword",
          "ENVIRONMENT": "?"
        }
    }
    

    并使用夹具来加载它们:

    from . import get_devices
    
    import os
    import json
    import pytest
    
    
    ENVIRONMENT_PATH = os.path.join(
        os.path.dirname(os.path.abspath(get_devices.__file__)),
        "../test.env.json"
    )
    
    
    @pytest.fixture()
    def test_environ():
        """Load environment variables to mock"""
        data = {}
        with open(ENVIRONMENT_PATH) as json_file:
            data = json.load(json_file)
        for (k, v) in data["GetDevicesFunction"].items():
            os.environ[k] = str(v)
        return data
    
    
    def test_lambda_handler(apigw_event, mocker, test_environ):   
        from get_devices import app
        ret = app.lambda_handler(apigw_event, "")  
        data = ret['body']['data'] 
        assert ret["statusCode"] == 200    
        assert len(data) > 0 
    

    如果你使用过sam build --use-container,并且你的数据库是在本地绑定到默认网络的docker容器中启动的,你也可以使用test.env.json文件来调用本地调用的函数:

    sam local invoke GetDevicesFunction --env-vars test.env.json --docker-network docker_default --debug
    

    使用sam build,您可以:

    sam local invoke GetDevicesFunction --env-vars test.env.json --debug
    

    【讨论】:

      猜你喜欢
      • 2019-05-10
      • 2018-12-16
      • 2018-06-14
      • 2021-04-15
      • 2022-11-18
      • 2020-09-29
      • 2020-03-22
      • 1970-01-01
      • 2023-03-10
      相关资源
      最近更新 更多