-
Notifications
You must be signed in to change notification settings - Fork 3
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
represent various objects when logging better #98
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 a lot of questions below, i'm not sure i fully understand the purpose of these changes.
430847f
to
0b91ae5
Compare
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.
Sorry, i had a few more things that i think we should address on this one before merging.
I now fully understand the reasoning and the benefit of adding this speech tool, and thank you for doing all this work for it! I think it could be the first of several narration-massaging tools that we could offer our users in the speech tools module.
assert [r.msg for r in caplog.records] == [ | ||
"Tester sees if all of 4 tests pass:", | ||
" Tester sees if fakeQuestion is equal to <True>.", | ||
" ... hoping it's equal to <True>.", | ||
" => <True>", | ||
" Tester sees if fakeQuestion is equal to <False>.", | ||
" ... hoping it's equal to <False>.", | ||
" => <False>", | ||
# don't be fooled! the next few lines do not have commas on purpose | ||
" ***ERROR***\n" | ||
"\n" | ||
"AssertionError: \n" | ||
"Expected: <False>\n" | ||
" but: was <True>\n", |
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 don't think this test belongs in this test class. I think we should put it in TestSee
.
I do think it's worth it to have a test that each of the Actions which now use represent_prop
are representing the props how we want them to be represented, but SeeAllOf
doesn't directly use it.
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 think this test isn't only confirming that represent_prop
does the right thing. It's a confluence of everything that goes into making SeeAllOf
work as expected.
- beat messages have to format and indent properly
- settings have to work properly
- See has to function properly
- SeeAllOf has to loop through each of the passed in tests.
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 think of it like this -- we want to prove SeeAllOf
calls See
, we can do that with another test showing how many times it gets called. But say someone tries to sneak in the following change:
the_actor.should(Silently(See.the(question, resolution)))
The test with the number of calls wouldn't catch that whereas this test would.
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 dunno, i think it's still in the wrong place and is (well, would be—we don't have the test that i'm suggesting in TestSee
) testing things that are already tested elsewhere.
I personally tend towards not having tests that are a group of tests we already have unless that integration is testing something new. I don't see anything new being tested with this test, aside from the test that is missing from TestSee
of course.
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.
In principle I agree that unit tests should be as atomic as possible. I've always struggled with testing functionality that basically coalesces multiple functionalities. Each of the smaller functions have been tested but how to you prove they all came together in the right manner? I found that at some point you'll get some overlap.
"We aren't proving the sugar is sweet, we are proving we used it in the recipe."
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 added two tests to See
to prove the logging works there as expected.
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.
For me, i want to know why a test at this level failed just by reading the title, if possible.
I think you might like functional testing, which test things as they would be used by a user, rather than all this fiddling with unit tests. So instead of testing each part down here, we'd have a test that was like:
def test_click_does_a_click(self, Tester):
target = Target.the("test").located_by("//test")
Tester.attempts_to(Click.on_the(Target))
# assert the dummy element was asked for by "//test"
# assert the dummy element had `.click()` called
It's a pretty interesting concept that turns the tests into more documentation on how the developers expect the tool to be used. If you remember a few years ago, the tests in here used to be kind of like that, until i realized i could just do Click().perform(Tester)
instead and skipped a layer.
I think the tests as they are now are easier to organize, but i did kind of like how those older tests looked.
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 can't find the article now that introduced me to the concept because i specifically remember it going on at length about how neat it is that someone wanting to use your library can just look at your tests directory to see how to use it. :(
I do remember i had found it right after i finished changing all the unit tests to how they are now, though, so i didn't want to change them all back. Haha.
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.
These logging tests are probably closer to functional rather than unit tests. I think the trick to any test suite (especially a public library) is no one way will suffice. We want as comprehensive as coverage as we can get.
For instance, if we were to completely rewrite SeeAllOf
to no longer use See
but we still wanted the logging to output the same, we need a test to show that it works as expected. This treats the action like a black box. There may be overlap at a unit test level, but only because we know what's inside that action.
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 want to know why a test at this level failed just by reading the title,
Perhaps I should rename these tests. Something like test_log_first_failure
?
def test_int(self): | ||
val = 1234 | ||
|
||
assert represent_prop(val) == "<1234>" |
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.
If we wanted to, we could parametrize this as test_everything_else
and verify that a bunch of different classes get wrapped in the <>
s.
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.
You're not wrong...
Separating them gives you a clear indication of where a bug lies. If somehow we only break how ints are displayed, when we run the tests TestRepresentProp:test_int
will inform us. While in this case that's not a significant difference in how we would go about fixing it, it is often helpful to have that separation to quickly diagnose a bug.
Code wise it's a couple extra lines. /shrug
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.
Parametrized tests still show what the values are, so we'd have that same level of granular feedback. We can also give each parameter'd test a specific name if we'd like to.
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.
Fair enough. I updated them.
I have a tendency to only use parameterized tests when there are 3 or more variations.
0f6bffa
to
6bcd0c2
Compare
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.
Some final thoughts and suggestions, but they're no longer blocking.
I'm excited to get this one in!
@@ -54,9 +55,9 @@ def as_(self: SelfMakeNote, key: str) -> SelfMakeNote: | |||
|
|||
def describe(self: SelfMakeNote) -> str: | |||
"""Describe the Action in present tense.""" | |||
return f"Make a note under {self.key}." | |||
return f"Make a note under {represent_prop(self.key)}." |
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 could be key_to_log
instead, just in case we change our mind on represent_prop
.
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.
(And it looks like that's the approach you have in the other describe
methods, to use *_to_log
.)
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've always worried that setting the variable in the constructor would somehow lead to a problem if anyone were to alter the attribute being logged after instantiation. It might be a non-issue but it's one of the reasons I try to avoid it as much as possible.
We want the value coming out of describe
to be accurate at the moment it is called.
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 forgot that part of the reason we have *_to_log
is for the beat
message. I kinda want to go through each of the classes and make those attributes properties.
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 think they will not need to be around any longer, ideally, once we figure out this whole update to the logging bit. Or maybe the description/beat itself becomes a property, i think that would be slick.
Either way, let's do the describe reimagining before we do a change like that, because it might not be necessary in that brave new world.
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 think that would be slick.
It really would. the only reason I didn't earlier is because the pattern wasn't already being used. I'll open an issue to add those at some point.
assert [r.msg for r in caplog.records] == [ | ||
"Tester sees if all of 4 tests pass:", | ||
" Tester sees if fakeQuestion is equal to <True>.", | ||
" ... hoping it's equal to <True>.", | ||
" => <True>", | ||
" Tester sees if fakeQuestion is equal to <False>.", | ||
" ... hoping it's equal to <False>.", | ||
" => <False>", | ||
# don't be fooled! the next few lines do not have commas on purpose | ||
" ***ERROR***\n" | ||
"\n" | ||
"AssertionError: \n" | ||
"Expected: <False>\n" | ||
" but: was <True>\n", |
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 dunno, i think it's still in the wrong place and is (well, would be—we don't have the test that i'm suggesting in TestSee
) testing things that are already tested elsewhere.
I personally tend towards not having tests that are a group of tests we already have unless that integration is testing something new. I don't see anything new being tested with this test, aside from the test that is missing from TestSee
of course.
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.
Re-approving! Thanks for the extra TLC!
34b9a96
to
860ab64
Compare
@perrygoy I updated those tests and rebased with trunk. I'm sorry to make you review it again. |
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.
Re-re-re-re-approving! I've got more where that came from.
* initial deps * initial project sync (via cruft) * ruff ANN * updated test based on changes in ScreenPyHQ/screenpy#98 * ruff ARG * ruff D * ruff EM * ruff FA * ruff I * ruff TCH * ruff PGH * ruff PIE * ruff PT * ruff RUF * ruff SIM * ruff UP * black formatting * fixing mypy namespace error * adding copyright tests * adding namespace test * fixing name of mocked item since it's being patched at the class level.
This is a fix for the way different objects are represented when logging.
repr
.describe_to
function will describe themselves.__str__
output with angle brackets<1>
,<True>
,<MyCustomObj>