| <!-- | |
| Licensed to the Apache Software Foundation (ASF) under one | |
| or more contributor license agreements. See the NOTICE file | |
| distributed with this work for additional information | |
| regarding copyright ownership. The ASF licenses this file | |
| to you under the Apache License, Version 2.0 (the | |
| "License"); you may not use this file except in compliance | |
| with the License. You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, | |
| software distributed under the License is distributed on an | |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| KIND, either express or implied. See the License for the | |
| specific language governing permissions and limitations | |
| under the License. | |
| --> | |
| # Testing Apache Hamilton code | |
| This is the runnable companion to the | |
| [Testing Hamilton code](https://hamilton.apache.org/how-tos/test-hamilton-code/) | |
| how-to. It shows that Hamilton functions are normal Python -- so the standard | |
| `pytest` patterns you already know apply, including when decorators are | |
| involved. | |
| The example covers the four cases from issue | |
| [#1044](https://github.com/apache/hamilton/issues/1044): | |
| 1. **Unit-testing plain functions** -- `test_my_functions.py` | |
| 2. **Unit-testing decorated functions** -- `test_decorated_functions.py` | |
| 3. **Integration-testing the DAG with `inputs=` and `overrides=`** -- `test_driver.py` | |
| 4. **In-memory modules with `ad_hoc_utils.create_temporary_module`** -- `test_ad_hoc_module.py` | |
| ## File organization | |
| | File | Purpose | | |
| | ---- | ------- | | |
| | `my_functions.py` | A small marketing dataflow (no decorators). | | |
| | `decorated_functions.py` | The same style of dataflow, using `@tag`, `@parameterize` and `@extract_columns`. | | |
| | `test_my_functions.py` | Unit tests that import and call functions directly. | | |
| | `test_decorated_functions.py` | Unit + driver-level tests for the decorated module. | | |
| | `test_driver.py` | End-to-end tests using `Builder().with_modules(...).build()` plus `inputs=` and `overrides=`. | | |
| | `test_ad_hoc_module.py` | Builds a module from inline-defined functions for self-contained tests. | | |
| | `conftest.py` | Adds this folder to `sys.path` so `import my_functions` works under pytest. | | |
| ## Running the tests | |
| ```bash | |
| pip install -r requirements.txt | |
| pytest | |
| ``` | |
| You should see all tests pass. Each test file is independently runnable: | |
| ```bash | |
| pytest test_my_functions.py -v | |
| pytest test_driver.py -v | |
| ``` | |
| ## What to take away | |
| * A Hamilton function is just a Python function. Testing it does **not** | |
| require building a Driver. | |
| * Decorators (`@tag`, `@parameterize`, `@extract_columns`, ...) leave the | |
| underlying callable intact. Direct function calls still work; the decorator | |
| changes how Hamilton wires the function into the DAG, not what the function | |
| computes. | |
| * For integration tests, `Builder().with_modules(...).build()` is the canonical | |
| entry point. Use `inputs=` to inject test data at the DAG inputs and | |
| `overrides=` to short-circuit intermediate nodes when you want to assert on | |
| downstream logic in isolation. | |
| * Need to test inline -- e.g. for a regression test or a custom materializer | |
| -- without a `.py` file on disk? Use | |
| `hamilton.ad_hoc_utils.create_temporary_module`. | |
| If you have questions, or need help with this example, | |
| join us on [Slack](https://join.slack.com/t/hamilton-opensource/shared_invite/zt-2niepkra8-DGKGf_tTYhXuJWBTXtIs4g). |