API calls and Testing
After some more work, I have results!
You should be able to check now at the bottom of my page for a little Visitor Counter!
I added in JavaScript to my page like so:
<script>
const API_URL = "https://******.execute-api.us-east-1.amazonaws.com/default/visitorcount";
async function fetchAndShowCount() {
try {
const res = await fetch(API_URL, { method: "POST" });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json(); // expects {"count": 123}
document.getElementById("visitor-count").textContent = `Visitor count: ${data.count}`;
} catch (err) {
console.error("Failed to fetch visitor count:", err);
document.getElementById("visitor-count").textContent = "Visitor count: Error";
}
}
window.addEventListener("DOMContentLoaded", fetchAndShowCount);
</script>
Essentially, I grabbed the public URL of my API Gateway that triggers the function and set that as a variable. Then, the fetchAndShowCount will make a POST call to the URL. It checks to see if the response is ok, and stores that as data. From there, it can display the count from that call that was returned by the API.
To actually call the function in the page, the event listener checks for the page load, and then calls it, effectively increasing the visitor count on page refresh.
I’d definitely love to make this to only count unique visitors, possibly by day, but that’s for the future!
Testing
I am completely new to writing tests for my code, so I asked for a little help (thanks chatgpt!). I was able to come up with three tests for this like so:
# test_lambda_handler.py
import json
import pytest
from unittest.mock import patch, MagicMock
import lambda_function # replace with your filename (without .py)
@pytest.fixture
def mock_table():
"""Fixture to mock DynamoDB table."""
mock_table = MagicMock()
return mock_table
@pytest.fixture
def mock_boto3_resource(mock_table):
"""Fixture to patch boto3.resource."""
with patch("lambda_function.dynamo") as mock_dynamo:
mock_dynamo.Table.return_value = mock_table
yield mock_dynamo
def test_lambda_handler_success(mock_boto3_resource, mock_table):
# Arrange
mock_table.update_item.return_value = {
"Attributes": {"visitorCount": 42}
}
event = {}
context = {}
# Act
response = lambda_function.lambda_handler(event, context)
# Assert
mock_table.update_item.assert_called_once_with(
Key=lambda_function.ITEM_KEY,
UpdateExpression="SET visitorCount = if_not_exists(visitorCount, :zero) + :inc",
ExpressionAttributeValues={":zero": 0, ":inc": 1},
ReturnValues="UPDATED_NEW"
)
assert response["statusCode"] == 200
body = json.loads(response["body"])
assert body["count"] == 42
def test_lambda_handler_failure(mock_boto3_resource, mock_table):
# Arrange: simulate DynamoDB throwing exception
mock_table.update_item.side_effect = Exception("DB is down")
event = {}
context = {}
# Act
response = lambda_function.lambda_handler(event, context)
# Assert
assert response["statusCode"] == 500
body = json.loads(response["body"])
assert body["error"] == "Internal Server Error"
assert "DB is down" in body["message"]
This will check two things:
- Confirms that the response is 200 OK and that the response contains the “count: 42”
- Simulates the DynamoDB table being down and whether or not the error message conveys the same
I’d still like to do some additional finagling with tests but this seemed like a good place to start!
Next steps
I may work on some more testing, but I’m excited to get some hands on with Automation and CI!