Test author guide
End-to-End Testing with VCR
This project uses a powerful "record and replay" testing strategy for its end-to-end (E2E) tests, powered by
pytest and the VCR.py library. This allows us to create high-fidelity tests based on real API interactions
while ensuring our CI/CD pipeline remains fast, reliable, and completely independent of a live API server.
The E2E tests are located in the ansible_waldur_generator/tests/e2e/ directory.
Core Concept: Cassette-Based Testing
-
Recording Mode: The first time a test is run, it requires access to a live Waldur API. The test executes its workflow (e.g., creating a VM), and
VCR.pyrecords every single HTTP request and its corresponding response into a YAML file called a "cassette" (e.g.,ansible_waldur_generator/tests/e2e/cassettes/test_create_instance.yaml). -
Replaying Mode: Once a cassette file exists, all subsequent runs of the same test will be completely offline.
VCR.pyintercepts any outgoing HTTP call, finds the matching request in the cassette, and "replays" the saved response. The test runs instantly without any network activity.
This approach gives us the best of both worlds: the realism of integration testing and the speed and reliability of unit testing.
Running the E2E Tests
The E2E tests are designed to be run in two distinct modes.
Mode 1: Replaying (Standard CI/CD and Local Testing)
This is the default mode. If the cassette files exist in
ansible_waldur_generator/tests/e2e/cassettes/, the tests will run
offline. This is the fastest and most common way to run the tests.
1 2 | |
This command should complete in a few seconds.
Mode 2: Recording (When Adding or Modifying Tests)
You only need to enter recording mode when you are:
- Creating a new E2E test.
- Modifying an existing E2E test in a way that changes its API interactions (e.g., adding a new parameter to a module call).
Workflow for Recording a Test:
-
Prepare the Live Environment: Ensure you have a live Waldur instance and that all the necessary prerequisite resources for your test exist (e.g., for creating a VM, you need a project, offering, flavor, image, etc.).
-
Set Environment Variables: Provide the test runner with the credentials for the live API. Never hardcode these in the test files.
1 2
export WALDUR_API_URL="https://your-waldur-instance.com/api/" export WALDUR_ACCESS_TOKEN="<your_real_api_token>" -
Delete the Old Cassette: To ensure a clean recording, delete the corresponding YAML file for the test you are re-recording.
pytest-vcrnames cassettes based on the test file and function name.1 2
# Example for the instance creation test rm ansible_waldur_generator/tests/e2e/cassettes/test_e2e_modules.py::TestInstanceModule::test_create_instance.yaml -
Run Pytest: Execute the test. It will now connect to the live API specified by your environment variables.
1 2
# Run a specific test to record its interactions poetry run pytest ansible_waldur_generator/tests/e2e/test_e2e_modules.py::TestInstanceModule::test_create_instanceAfter the test passes, a new cassette file will be generated.
-
Review and Commit:
- CRITICAL: Inspect the newly generated
.yamlcassette file. - Verify that sensitive data, like the
Authorizationtoken, has been automatically scrubbed and replaced with a placeholder (e.g.,DUMMY_TOKEN). This is configured inpyproject.tomlorpytest.ini. - Commit the new or updated cassette file to your Git repository along with your test code changes.
Writing a New E2E Test
Follow the pattern established in ansible_waldur_generator/tests/e2e/:
- Organize with Classes: Group tests for a specific module into a class (e.g.,
TestVolumeModule). - Use the
@pytest.mark.vcrDecorator: Add this decorator to your test class or individual test methods to enable VCR. - Use the
auth_paramsFixture: This fixture provides the standardapi_urlandaccess_tokenparameters, reading them from environment variables during recording and using placeholders during replay. - Use the
run_module_harness: This generic helper function handles the boilerplate of mockingAnsibleModuleand running the module'smain()function. - Write Your Test Logic:
- Arrange: Define the
user_paramsdictionary that simulates the Ansible playbook input. - Act: Call the
run_module_harness, passing it the imported module object and theuser_params. - Assert: Check the
exit_resultandfail_resultto verify that the module behaved as expected (e.g.,changedisTrue, the returnedresourcehas the correct data).
Example Skeleton:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |