[英]Running pytest in AWS SAM doesn't use env vars in template.yaml
I'm testing my lambda with pytest.我正在用 pytest 测试我的 lambda。 My lambda, GetDevicesFunction , connects to a database using a method from a shared module, aurora , located in the
utils_layer/python
directory.我的 lambda GetDevicesFunction使用来自
utils_layer/python
目录中的共享模块aurora的方法连接到数据库。 The parameters for the database connection come from template.yaml .数据库连接的参数来自template.yaml 。 Everything works fine when I run
sam build && sam local invoke
but when I run my pytest , the environment vars don't seem to get pulled in from my template.当我运行
sam build && sam local invoke
时一切正常,但是当我运行pytest时,似乎没有从我的模板中提取环境变量。 Is this expected or am I missing something?这是预期的还是我错过了什么?
Here is my project...这是我的项目...
# 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
Any suggestions on how to pull in env vars from my template would be appreciated.任何有关如何从我的模板中提取环境变量的建议都将不胜感激。
I did something like this in conftest.py
:我在
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)
You can use the same env.json intended to invoke the function locally .您可以使用相同的 env.json 来在本地调用 function 。 In this case I use one specifically for test called
test.env.json
(excluded from source control in .gitignore
) with the environment parameters, with this format:在这种情况下,我使用一个专门用于测试的名为
test.env.json
(从.gitignore
的源代码控制中排除)与环境参数,格式如下:
{
"GetDevicesFunction": {
"DATABASE_NAME": "dbname",
"DATABASE_HOST": "dbhost",
"DATABASE_PORT": "5432",
"DATABASE_USER": "dbuser",
"DATABASE_PASSWORD": "dbpassword",
"ENVIRONMENT": "?"
}
}
and use a fixture to load them:并使用夹具来加载它们:
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
If you have used sam build --use-container
, and your database is launched locally in a docker container bound to the default network, you can also use the test.env.json
file to invoke the function locally calling:如果您使用了
sam build --use-container
,并且您的数据库是在绑定到默认网络的 docker 容器中本地启动的,您还可以使用test.env.json
文件在本地调用 ZC1C425268E17385D14ZA5074F:
sam local invoke GetDevicesFunction --env-vars test.env.json --docker-network docker_default --debug
and with sam build
you can:使用
sam build
你可以:
sam local invoke GetDevicesFunction --env-vars test.env.json --debug
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.