This time, I would like to verify on the following assumptions.
In addition, if you use SAM, you can build DynamoDB etc. in a pseudo local environment using Docker environment, but this time, I would like to verify the test with an eye on unit test (future automatic test). I think.
So, first of all, create a project. If you are not sure about creating a project with SAM, please do so in advance.
-[AWS] Serverless Application Model (SAM) Basic Summary -[AWS] Create API Gateway + Lambda + DynamoDB sample with Serverless Application Model (SAM)
We recommend that you read through.
$ sam init --runtime=python3.8
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Project name [sam-app]:
Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git
AWS quick start application templates:
1 - Hello World Example
2 - EventBridge Hello World
3 - EventBridge App from scratch (100+ Event Schemas)
4 - Step Functions Sample App (Stock Trader)
5 - Elastic File System Sample App
Template selection: 1
-----------------------
Generating application:
-----------------------
Name: sam-app
Runtime: python3.8
Dependency Manager: pip
Application Template: hello-world
Output Directory: .
Next steps can be found in the README file at ./sam-app/README.md
Install the Python packages needed to run unit tests.
$ pipenv install pytest pytest-mock mocker moto --dev
You can call the Lamnda function with the following command to run a unit test for each Lambda function. First, let's run a test of the Hello World Lambda function that is created by default (this test code is also created by default).
$ python -m pytest tests
============================= test session starts ==============================
platform darwin -- Python 3.8.5, pytest-6.0.1, py-1.9.0, pluggy-0.13.1
rootdir: /Users/******/aws/github/sam-app
plugins: mock-3.3.0
collected 1 item
tests/unit/test_handler.py . [100%]
============================== 1 passed in 0.02s ===============================
You can see that one test was successful.
This time, since the purpose is only to unit test, we will make only the changes necessary for the test without changing the SAM settings. Now, let's add a process to access (add a record) DynamoDB.
hello_world/app.py
import json
import boto3
import os
from datetime import datetime
def lambda_handler(event, context):
try:
event_body = json.loads(event["body"])
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("Demo")
table.put_item(
Item={
"Key": event_body["test"],
"CreateDate": datetime.utcnow().isoformat()
}
)
return {
"statusCode": 200,
"body": json.dumps({
"message": "hello world",
}),
}
except Exception as e:
return {
"statusCode": 500,
"body": json.dumps({
"message": e.args
}),
}
tests/unit/test_handler.py
import boto3
import json
import pytest
from hello_world import app
from moto import mock_dynamodb2
@pytest.fixture()
def apigw_event():
""" Generates API GW Event"""
return {
"body": '{ "test": "body"}',
"resource": "/{proxy+}",
"requestContext": {
"resourceId": "123456",
"apiId": "1234567890",
"resourcePath": "/{proxy+}",
"httpMethod": "POST",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
"accountId": "123456789012",
"identity": {
"apiKey": "",
"userArn": "",
"cognitoAuthenticationType": "",
"caller": "",
"userAgent": "Custom User Agent String",
"user": "",
"cognitoIdentityPoolId": "",
"cognitoIdentityId": "",
"cognitoAuthenticationProvider": "",
"sourceIp": "127.0.0.1",
"accountId": "",
},
"stage": "prod",
},
"queryStringParameters": {"foo": "bar"},
"headers": {
"Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
"Accept-Language": "en-US,en;q=0.8",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Mobile-Viewer": "false",
"X-Forwarded-For": "127.0.0.1, 127.0.0.2",
"CloudFront-Viewer-Country": "US",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
"X-Forwarded-Port": "443",
"Host": "1234567890.execute-api.us-east-1.amazonaws.com",
"X-Forwarded-Proto": "https",
"X-Amz-Cf-Id": "aaaaaaaaaae3VYQb9jd-nvCd-de396Uhbp027Y2JvkCPNLmGJHqlaA==",
"CloudFront-Is-Tablet-Viewer": "false",
"Cache-Control": "max-age=0",
"User-Agent": "Custom User Agent String",
"CloudFront-Forwarded-Proto": "https",
"Accept-Encoding": "gzip, deflate, sdch",
},
"pathParameters": {"proxy": "/examplepath"},
"httpMethod": "POST",
"stageVariables": {"baz": "qux"},
"path": "/examplepath",
}
@mock_dynamodb2
def test_lambda_handler(apigw_event, mocker):
dynamodb = boto3.resource('dynamodb')
dynamodb.create_table(
TableName='Demo',
KeySchema=[
{
'AttributeName': 'Key',
'KeyType': 'HASH'
},
{
'AttributeName': 'CreateDate',
'KeyType': 'RANGE'
}
],
AttributeDefinitions=[
{
'AttributeName': 'Key',
'AttributeType': 'S'
},
{
'AttributeName': 'CreateDate',
'AttributeType': 'S'
}
],
ProvisionedThroughput={
'ReadCapacityUnits': 10,
'WriteCapacityUnits': 10
}
)
ret = app.lambda_handler(apigw_event, "")
data = json.loads(ret["body"])
assert ret["statusCode"] == 200
assert "message" in ret["body"]
assert data["message"] == "hello world"
# assert "location" in data.dict_keys()
The main corrections are
--Addition of definitions required for import
--Added @ mock_dynamodb2
before test_lambda_handler
--Create a DynamoDB table in a pseudo manner before calling the Lambda function with test_lambda_handler
is.
Now, let's run the unit test immediately.
$ python -m pytest tests
============================= test session starts ==============================
platform darwin -- Python 3.8.5, pytest-6.0.1, py-1.9.0, pluggy-0.13.1
rootdir: /Users/******/aws/github/sam-app
plugins: mock-3.3.0
collected 1 item
tests/unit/test_handler.py . [100%]
=============================== warnings summary ===============================
[pytest]
/Users/******/.local/share/virtualenvs/sam-app-3Tr4jFKA/lib/python3.8/site-packages/boto/plugin.py:40
/Users/******/.local/share/virtualenvs/sam-app-3Tr4jFKA/lib/python3.8/site-packages/boto/plugin.py:40: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
import imp
/Users/******/.local/share/virtualenvs/sam-app-3Tr4jFKA/lib/python3.8/site-packages/moto/cloudformation/parsing.py:407
/Users/******/.local/share/virtualenvs/sam-app-3Tr4jFKA/lib/python3.8/site-packages/moto/cloudformation/parsing.py:407: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working
class ResourceMap(collections.Mapping):
-- Docs: https://docs.pytest.org/en/stable/warnings.html
It's been successful, but I'm getting a warning.
This means that in the current version of Python, some functions used by moto etc. have been stopped.
This warning does not affect the unit test, so create a file with the name pytest.ini
in the project home, write the following and save it.
pytest.ini
[pytest]
filterwarnings =
ignore::DeprecationWarning
Let's try again.
$ python -m pytest tests
============================= test session starts ==============================
platform darwin -- Python 3.8.5, pytest-6.0.1, py-1.9.0, pluggy-0.13.1
rootdir: /Users/******/aws/github/sam-app, configfile: pytest.ini
plugins: mock-3.3.0
collected 1 item
tests/unit/test_handler.py . [100%]
============================== 1 passed in 2.10s ===============================
This time it ended without warning.
It is desirable that unit tests are automatically executed when the source code is changed.
This time, since the unit test is executed locally, only the source code part is modified, but if it is an extension of the CI / CD pipeline, it should be in requirements.txt
or each definition file. Also needs to be fixed.
Next time, I would like to do verification at some point, including the connection with the pipeline in that area.
https://github.com/hito-psv/test-demo-001
Recommended Posts