-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding Pytest Tests #65
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have reviewed five files! I think that is a representative set of the whole, but if there are things you think I should have looked at that got radio silence then let me know.
It's very possible that some of my complaints here come from being unfamiliar with pytest, but the things that are in here feel very fragile to me.
dev-requirements.txt
Outdated
dbt-core==1.2.0rc1 | ||
dbt-redshift==1.2.0rc1 | ||
dbt-snowflake==1.2.0rc1 | ||
dbt-bigquery==1.2.0rc1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will these stay up to date over time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll manually update these based on the version that we want to test. Right now its the RC candidate but we'll update it to v1.2 once it is no longer RC.
@@ -1,4 +1,4 @@ | |||
select | |||
* | |||
,round(order_total - (order_total/2)) as discount_total | |||
,1 as discount_total |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ this is weird, shouldn't it be a percentage or something? I just spent a bunch of time digging through the files trying to work out why the average discount was always 1 in your expectation seed.
The thing that worries me here is that if your source data isn't that varied then you don't actually get the benefit of testing the aggregations - what if something has gone horribly wrong and you're actually calculating max/min or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good point - I was trying to keep it as simple as possible but if it doesn't represent the actual calculation then thats not helpful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh actually this is still a holdover from integration testing! These will mainly just be used for local development as we move the CI testing to pytest to test functionality. So the old version with round is retained in the pytest models 👯
2022-01-01,FALSE,1.00000000000000000000 | ||
2022-02-01,FALSE,1.00000000000000000000 | ||
2022-02-01,TRUE,1.00000000000000000000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't testing much of anything (as mentioned above)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fixed in the pytest framework - everything within the integration tests folder is a holdover from when we were doing it that way and I'm hesitant to delete anything until we've got it all migrated over.
macros/secondary_calculations/secondary_calculation_period_over_period.sql
Show resolved
Hide resolved
base_average_metric_yml = """ | ||
version: 2 | ||
models: | ||
- name: base_average_metric | ||
tests: | ||
- dbt_utils.equality: | ||
compare_model: ref('base_average_metric__expected') | ||
metrics: | ||
- name: base_average_metric | ||
model: ref('fact_orders') | ||
label: Total Discount ($) | ||
timestamp: order_date | ||
time_grains: [day, week, month] | ||
type: average | ||
sql: discount_total | ||
dimensions: | ||
- had_discount | ||
- order_country | ||
""" | ||
|
||
# seeds/base_average_metric__expected.csv | ||
base_average_metric__expected_csv = """ | ||
date_month,base_average_metric | ||
2022-01-01,1.00000000000000000000 | ||
2022-02-01,1.3333333333333333 | ||
""".lstrip() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okkkkkk so am I understanding this correctly:
These lines of hardcoded logic are the same as the ones that are up above as proper .sql/.yml/.csv files? So we're duplicating everything? How do they stay in sync? And why do we need the former if it's all just turning into string constants down here?
idk if this is pythonic or not, but I would strongly prefer something like
#https://stackoverflow.com/a/49564464/14007029
from pathlib import Path
base_average_metric_sql = Path('../models/base_average_metric.sql').read_text();
base_average_metric_yml = Path('../models/base_average_metric.yml').read_text();
Such that we don't have to redefine everything from scratch in two places 🤢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite. There are static datasets/definitions contained in fixtures.py
and those are then imported. The reason that I keep some components outside of it (like the definition of the metric) is so we can understand what metric is being tested while just looking at the single file.
If we're trying to be more pythonic then I should move that definition into the fixtures file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK! I think I was caught up on thinking that the stuff in the integration tests project was this stuff's twin. Carry on!
|
||
# test tests | ||
results = run_dbt(["test"]) # expect passing test | ||
assert len(results) == 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the results
object here? Is it an array of passing tests? I would sorta prefer that it returned all results, and a pass/fail, and then we could do whatever the python version of results.all(result => result.statuscode == 'PASS')
is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because otherwise anytime we add new tests, we have to go through and update the expected number of passes. (or, worse, we add a test which fails, we forget to update the expected count, and it sits there failing forever).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dbeatty10 would have more context around this area. I suspect it returns the results of each command and then we confirm that it matches what we expect. So if we added a new test we'd want to assert that only two tests were running
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The example containing hard-coded numbers comes from here.
dbt_metrics
doesn't need to do it that way if it doesn't want to. The important part is to verify that it works as expected for all the possible edge cases. And that it fails in the ways expected with bad input.
result_statuses = sorted(r.status for r in results) | ||
assert result_statuses == ["pass"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK maybe that's what this line is? but then I don't know what the above stuff is, and I still feel very uncomfortable about hardcoded numbers of things in test files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Deleted my original comment here and moved it above)
Description
This PR adds
pytest
tests. It does NOT implement them as part of CI. That will take place in a following PR so that people don't have to review one massive PR and its instead 1 massive PR and one tiny PR. Wait....Test Types Added: