blob: 6d50aeaa1d5675071484c532748cd818b219b2d5 [file]
# 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.
"""Integration tests that exercise the full DAG via the Driver.
These show three patterns:
1. Build a Driver from one or more modules and assert on its outputs.
2. Use ``inputs=`` to inject test data at the DAG inputs.
3. Use ``overrides=`` to short-circuit an intermediate node, so you can test
downstream logic in isolation without recomputing upstream nodes.
"""
import my_functions
import pandas as pd
from hamilton import driver
def _build_driver() -> driver.Driver:
"""Helper: construct a Driver pointed at our module under test."""
return driver.Builder().with_modules(my_functions).build()
def test_driver_executes_full_pipeline() -> None:
dr = _build_driver()
inputs = {
"raw_signups": pd.Series([0, 1, 10, 50, 100, 200, 400]),
"raw_spend": pd.Series([0, 10, 10, 20, 40, 40, 50]),
}
result = dr.execute(["spend_per_signup", "spend_mean"], inputs=inputs)
# spend after dropping the header row: [10, 10, 20, 40, 40, 50] -> mean 28.333...
assert result["spend_mean"] == pd.Series([10, 10, 20, 40, 40, 50]).mean()
pd.testing.assert_series_equal(
result["spend_per_signup"],
pd.Series([10.0, 1.0, 0.4, 0.4, 0.2, 0.125]),
check_names=False,
)
def test_overrides_short_circuit_upstream_nodes() -> None:
"""`overrides=` lets us pin a node's value, so upstream code is skipped.
This is the integration-test sweet spot: instead of fabricating realistic
raw inputs and re-deriving every intermediate, we hand the DAG a known
`spend` and assert on the *downstream* arithmetic.
"""
dr = _build_driver()
result = dr.execute(
["spend_zero_mean_unit_variance"],
# No `inputs=` needed because every upstream dependency is overridden.
overrides={"spend": pd.Series([10.0, 20.0, 30.0])},
)
scaled = result["spend_zero_mean_unit_variance"]
# Standardised series: mean ~0, std ~1.
assert abs(scaled.mean()) < 1e-9
assert abs(scaled.std(ddof=1) - 1.0) < 1e-9
def test_what_is_upstream_of() -> None:
"""The Driver itself can be inspected -- handy for asserting graph shape."""
dr = _build_driver()
upstream_node_names = {n.name for n in dr.what_is_upstream_of("spend_per_signup")}
# `spend_per_signup` depends (transitively) on the raw inputs and on `spend`/`signups`.
assert {"spend", "signups", "raw_spend", "raw_signups"} <= upstream_node_names