Testing and Debugging
The tests
directory demonstrates how unit testing is used to ensure the code’s functionality and correctness.
Unit Testing
Unit tests are designed to verify the behavior of individual functions or components. They provide a framework for ensuring that each part of the codebase works as expected. The tests/test_exporter.py
file contains several unit tests for the BalenaCollector
class.
Example:
@mock.patch("main.BalenaCollector.get_fleet_metrics")
def test_collect(self, mock_metrics):
with mock.patch.object(self.collector, "get_balena_fleets") as mock_fleets:
mock_fleets.return_value = ["fleet1", "fleet2"]
mock_metrics.side_effect = [("test_fleet", 3), ("test_fleet", 3)]
result = list(self.collector.collect()[0].samples)
expected = [('balena_devices_online', {'fleet_name': 'test_fleet'}, 3),
('balena_devices_online', {'fleet_name': 'test_fleet'}, 3)]
result = [(s.name, s.labels, s.value) for s in result]
self.assertEqual(result, expected)
Source: tests/test_exporter.py
Explanation:
The test uses mock
to simulate the behavior of the get_balena_fleets
and get_fleet_metrics
methods, setting their return values. It then calls the collect
method, compares the actual result with the expected output, and uses self.assertEqual
to verify the expected behavior.
Mocking and Patching
The mock
library is used to create mock objects that mimic the behavior of real objects. Patching allows replacing functions or methods with mock objects, enabling the isolation of code under test.
Example:
with mock.patch("main.requests.get") as mock_get:
mock_get.return_value.ok = True
mock_get.return_value.json.return_value = {"d": [{"id": "fleet1"}, {"id": "fleet2"}]}
result = list(self.collector.get_balena_fleets())
expected = ["fleet1", "fleet2"]
self.assertEqual(result, expected)
Source: tests/test_exporter.py
Explanation:
This test patches the requests.get
function, simulating a successful API call and setting the response content.
Debugging
Debugging is the process of identifying and resolving errors within the codebase.
Common Debugging Tools:
Print statements: Simple but effective for tracing code execution and variable values.
Debugging tools: Integrated debuggers (such as pdb in Python) provide tools for stepping through code, inspecting variables, and setting breakpoints.
Logging: Logging statements can be used to record events and debug information, making it easier to analyze issues over time.
Running Tests
To run the unit tests, use the following command:
python -m unittest tests/test_exporter.py
Source: tests/test_exporter.py
Explanation:
The unittest
module is used to execute the unit tests.
Project Setup
Dependencies:
Python
Requirements: The
requirements.txt
file lists all necessary dependencies.
certifi==2024.7.4
charset-normalizer==3.1.0
idna==3.4
prometheus-client==0.16.0
requests==2.31.0
urllib3==1.26.18
Source: requirements.txt
Running the Application:
- Build the Docker image.
docker build -t balena-exporter .
- Run the Docker container.
docker run -d \
--name balena-exporter \
-p 8000:8000 \
-e BALENA_TOKEN= \
balena-exporter
Source: README.md
Explanation:
The Docker image is built using the Dockerfile
in the repository. The container is run with the BALENA_TOKEN
environment variable set to your Balena API token.
Top-Level Directory Explanations
tests/ - This directory contains all the unit and integration tests for the project. It includes the __init__.py
file which makes it a package, and specific test files like test_exporter.py
.