Fix: Unit test CTE failures not being captured#5081
Conversation
| failed_subtest = "" | ||
|
|
||
| if subtest := getattr(self, "_subtest", None): | ||
| if cte := subtest.params.get("cte"): | ||
| failed_subtest = f" (CTE {cte})" |
There was a problem hiding this comment.
This is to differentiate the CTE mismatch from the output one, e.g if both subtests fail in a single test that'd be the output:
FF
======================================================================
----------------------------------------------------------------------
FAIL: test_example_full_model (/Users/vaggelisd/Desktop/tobiko/test_dir/tests/test.yaml)
----------------------------------------------------------------------
Data mismatch (CTE "filtered_orders_cte")
┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃ Row ┃ id: Expected ┃ id: Actual ┃
┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩
│ 0 │ 5 │ 1 │
└──────────┴─────────────────────────┴─────────────────────┘
----------------------------------------------------------------------
FAIL: test_example_full_model (/Users/vaggelisd/Desktop/tobiko/test_dir/tests/test.yaml)
----------------------------------------------------------------------
Data mismatch
┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃ Row ┃ id: Expected ┃ id: Actual ┃
┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩
│ 0 │ 2 │ 1 │
└──────────┴─────────────────────────┴─────────────────────┘
----------------------------------------------------------------------
Test Failure Summary
======================================================================
Ran 1 tests against duckdb in 0.14 seconds.
Failed tests (1):
• /Users/vaggelisd/Desktop/tobiko/test_dir/tests/test.yaml::test_example_full_model
======================================================================| def get_fail_and_error_tests(self) -> t.List[ModelTest]: | ||
| # If tests contain failed subtests (e.g testing CTE outputs) we don't want | ||
| # to report it as different test failures | ||
| test_name_to_test = { | ||
| test.test_name: test | ||
| for test, _ in self.failures + self.errors | ||
| if isinstance(test, ModelTest) | ||
| } | ||
| return list(test_name_to_test.values()) |
There was a problem hiding this comment.
Simple abstraction to simplify console.py
| copy_test_file(original_test_file, tmp_path / "tests" / f"test_success_{i}.yaml", i) | ||
| copy_test_file(new_test_file, tmp_path / "tests" / f"test_failure_{i}.yaml", i) |
There was a problem hiding this comment.
Previously we copied these files directly but unit tests don't work well if the name is duplicated, I'll follow up with a UX fix
| errors = result.errors | ||
| failures = result.failures | ||
| skipped = result.skipped | ||
|
|
||
| infos = [] | ||
| if failures: | ||
| infos.append(f"failures={len(failures)}") | ||
| if errors: | ||
| infos.append(f"errors={len(errors)}") | ||
| if skipped: | ||
| infos.append(f"skipped={skipped}") |
There was a problem hiding this comment.
dead code, infos is no longer used
a726d55 to
681d328
Compare
|
|
||
| failed_subtest = "" | ||
|
|
||
| if subtest := getattr(self, "_subtest", None): |
There was a problem hiding this comment.
I wonder if it even makes sense at this point to rely on subtests rather than write our own context manager which sets the current CTE and then resets it.
There was a problem hiding this comment.
Are there any other benefits that subtests offer us here?
There was a problem hiding this comment.
I had the same thought as I was working on this, I think it's merit is that it triggers the callback addSubTest when it finishes, otherwise we'd have to schedule that ourselves.
Not really a strong argument for it, but I'm not sure if there's a strong argument against it either. Happy to reconsider if you had a better thought with the custom CM
Fixes #5067
In
unittestinternalsTestCase::addSubTestappends the result of that subtest directly toself.errorsorself.failures:However, our custom classes expected that this'd happen through
addError(...)oraddFailure(...)so that we can intercept these exceptions (since they get turned into strings before being stored in their parent classes).To solve this regression,
ModelTestTextResult::addSubTestnow follows the parent implementation and instead calls the respective APIs which do fill our ownoriginal_failures&original_errorstructures.The output for that looks like:
Docs
unittest.TestResult | unittest.TestCase | unittest.runner