-
Notifications
You must be signed in to change notification settings - Fork 20
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
Refactor test_transfor.py
: pass variables to pytest parametrize, use conftest.py
for common error messages
#236
Conversation
Warning! No news item is found for this PR. If this is a user-facing change/feature/fix, |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #236 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 8 8
Lines 362 372 +10
=========================================
+ Hits 362 372 +10
|
@@ -36,3 +36,21 @@ def _load(filename): | |||
@pytest.fixture | |||
def do_minimal_tth(): | |||
return DiffractionObject(wavelength=2 * np.pi, xarray=np.array([30, 60]), yarray=np.array([1, 2]), xtype="tth") | |||
|
|||
|
|||
@pytest.fixture |
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 following two warning messages are used often in test_transform.py
We could have access to this string within tests
via conftest? We aren't really importing from src
... so I think it really increases readability for some test cases (not all):
For example
From:
params_d_to_tth_bad = [
# UC1: user specified invalid d values that result in tth > 180 degrees
(
[4 * np.pi, np.array([1.2, 1, 0.8, 0.6, 0.4, 0.2])],
[
ValueError,
"The supplied input array and wavelength will result in an impossible two-theta. "
"Please check these values and re-instantiate the DiffractionObject with correct values.",
],
),
# UC2: user specified a wrong wavelength that result in tth > 180 degrees
(
[100, np.array([1, 0.8, 0.6, 0.4, 0.2, 0])],
[
ValueError,
"The supplied input array and wavelength will result in an impossible two-theta. "
"Please check these values and re-instantiate the DiffractionObject with correct values.",
],
),
]
@pytest.mark.parametrize("inputs, expected", params_d_to_tth_bad)
def test_d_to_tth_bad(inputs, expected):
with pytest.raises(expected[0], match=expected[1]):
d_to_tth(inputs[1], inputs[0])
to:
@pytest.mark.parametrize(
"wavelength, d, expected_error_type",
[
# UC1: user specified invalid d values that result in tth > 180 degrees
(4 * np.pi, np.array([1.2, 1, 0.8, 0.6, 0.4, 0.2]), ValueError),
# UC2: user specified a wrong wavelength that result in tth > 180 degrees
(100, np.array([1, 0.8, 0.6, 0.4, 0.2, 0]), ValueError),
],
)
def test_d_to_tth_bad(wavelength, d, expected_error_type, invalid_q_or_d_or_wavelength_error_msg):
expected_error_msg = invalid_q_or_d_or_wavelength_error_msg
with pytest.raises(expected_error_type, match=expected_error_msg):
d_to_tth(d, wavelength)
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.
Yes on that
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.
No numbers have been modified.
Here primarily removed extra variables such as params_q_to_tth
. First thought was that, it would be best to not have variables shared globally at the file level.
Second thought was that
@pytest.mark.parametrize(
"wavelength, q, expected_tth",
[
# UC1: Empty q values, no wavelength, return empty arrays
(None, np.empty((0)), np.empty((0))),
Feels more natural - the first two lines are like - "hey these are testing variables that we provide. The the last parameter is what we expect. Of course, if you are interested... take a look at the rest of the lines for specific UCs."
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.
Yes, I agree. In future we would like to spend 75% of thinking/planning/reviewing time on the tests, then 25% on the code target than vice versa and so the more the tests are readable and interpretable and syntactically codified the better. This dress well, as you describe with an extra comment that captures the behavioral intent.
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.
Even better for the behavior would be something like. "Minimally support empty arrays"
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.
Or both comments, behavior and example of behavior
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.
@pytest.mark.parametrize(
"wavelength, q, expected_tth",
[
# UC1.1: User specified empty 'q' and no 'wavelength'.
# Expect empty 'tth' array and UserWarning about missing wavelength.
(None, np.empty((0)), np.empty((0))),
# UC1.2: User specified empty 'q' and 'wavelength'. Expect empty 'tth' array.
(4 * np.pi, np.empty((0)), np.empty(0)),
# UC2.1: User specified non-empty 'q' values and no 'wavelength'.
# Expect non-empty 'tth' array and UserWarning about missing wavelength.
(
None,
np.array([0, 0.2, 0.4, 0.6, 0.8, 1]),
np.array([0, 1, 2, 3, 4, 5]),
),
# UC2.2: User specified non-empty 'q' values and 'wavelength'.
# Expect tth values are 2*arcsin(q) in degrees.
(4 * np.pi, np.array([0, 1 / np.sqrt(2), 1.0]), np.array([0, 90.0, 180.0])),
],
)
This is the change made since the last commit - it's hard to read below, so pasting the change here. How do you like it? I've used UC1.1 and UC1.2 (w/o q
) vs. UC2.1 and UC2.2 (with q
).
Also having a blank line between each UC feels a bit more readable?
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.
@sbillinge ready for review - this is the only change made since your last review. (It's getting a bit big..)
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 not quite what I had in mind. I think it was good before, but just needed a kind of overriding framing. Also, this is not the correct use of UCs per how we do it in the group, so I don't want to propagate wrong ideas by calling these UCs.
Maybe it is easier if I put what I had in mind below
@pytest.mark.parametrize(
"wavelength, q, expected_tth",
[
# Case 1: Allow empty arrays
# 1. Empty q values, no wavelength, return empty arrays
(None, np.empty((0)), np.empty((0))),
# 2: Empty q values, wavelength specified, return empty arrays
(4 * np.pi, np.empty((0)), np.empty(0)),
# Case 2: allow wavelength to be missing.
# 3: user specified valid q values, no wavelength, return index array
(
None,
np.array([0, 0.2, 0.4, 0.6, 0.8, 1]),
np.array([0, 1, 2, 3, 4, 5]),
),
# Case 3: correctly specified q and wavelength
# expected tth values are 2*arcsin(q) in degrees
(4 * np.pi, np.array([0, 1 / np.sqrt(2), 1.0]), np.array([0, 90.0, 180.0])),
],
)
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.
@pytest.mark.parametrize(
"wavelength, q, expected_tth",
[
# Case 1: Allow empty arrays for q
# 1. Empty q values, no wavelength, return empty arrays
(None, np.empty((0)), np.empty((0))),
# 2. Empty q values, wavelength specified, return empty arrays
(4 * np.pi, np.empty((0)), np.empty(0)),
# Case 2: Allow wavelength to be missing.
# Valid q values, no wavelength, return index array
(
None,
np.array([0, 0.2, 0.4, 0.6, 0.8, 1]),
np.array([0, 1, 2, 3, 4, 5]),
),
# Case 3: Correctly specified q and wavelength
# Expected tth values are 2*arcsin(q) in degrees
(4 * np.pi, np.array([0, 1 / np.sqrt(2), 1.0]), np.array([0, 90.0, 180.0])),
],
)
Thank you @sbillinge. Above is used for one of the refactored test functions. For other test functions, I could further make improvements via new PRs. Adding more here is a bit hard for you and me to track things..
assert np.allclose(actual_q, expected_q) | ||
|
||
|
||
params_tth_to_q_bad = [ |
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.
Lots of red lines above/below, but again, no manual fixes have been done..
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.
Ready for review! @sbillinge and cc'ing @yucongalicechen for your second look!
test_transfor.py
: pass variables to functions, maintain function scope, use conftest.py
for common error messagestest_transfor.py
: pass variables to pytest parametrize, use conftest.py
for common error messages
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.
Yes, I like this very much. Two thumbs up. Please see my inline comments, but let's use this as our own kind of testing PEP.
For the behavior/example we could group then with s blank line of there are a few different rxamples of the same behavior.
@@ -36,3 +36,21 @@ def _load(filename): | |||
@pytest.fixture | |||
def do_minimal_tth(): | |||
return DiffractionObject(wavelength=2 * np.pi, xarray=np.array([30, 60]), yarray=np.array([1, 2]), xtype="tth") | |||
|
|||
|
|||
@pytest.fixture |
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.
Yes on that
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.
Yes, I agree. In future we would like to spend 75% of thinking/planning/reviewing time on the tests, then 25% on the code target than vice versa and so the more the tests are readable and interpretable and syntactically codified the better. This dress well, as you describe with an extra comment that captures the behavioral intent.
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.
Even better for the behavior would be something like. "Minimally support empty arrays"
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.
Or both comments, behavior and example of behavior
@sbillinge ready for review - made a change shown in the latest commit below and replied to your in-line comments. |
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.
please see reply to your comment
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 not quite what I had in mind. I think it was good before, but just needed a kind of overriding framing. Also, this is not the correct use of UCs per how we do it in the group, so I don't want to propagate wrong ideas by calling these UCs.
Maybe it is easier if I put what I had in mind below
@pytest.mark.parametrize(
"wavelength, q, expected_tth",
[
# Case 1: Allow empty arrays
# 1. Empty q values, no wavelength, return empty arrays
(None, np.empty((0)), np.empty((0))),
# 2: Empty q values, wavelength specified, return empty arrays
(4 * np.pi, np.empty((0)), np.empty(0)),
# Case 2: allow wavelength to be missing.
# 3: user specified valid q values, no wavelength, return index array
(
None,
np.array([0, 0.2, 0.4, 0.6, 0.8, 1]),
np.array([0, 1, 2, 3, 4, 5]),
),
# Case 3: correctly specified q and wavelength
# expected tth values are 2*arcsin(q) in degrees
(4 * np.pi, np.array([0, 1 / np.sqrt(2), 1.0]), np.array([0, 90.0, 180.0])),
],
)
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.
@sbillinge ready for review - replied to your in-line comment
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.
@pytest.mark.parametrize(
"wavelength, q, expected_tth",
[
# Case 1: Allow empty arrays for q
# 1. Empty q values, no wavelength, return empty arrays
(None, np.empty((0)), np.empty((0))),
# 2. Empty q values, wavelength specified, return empty arrays
(4 * np.pi, np.empty((0)), np.empty(0)),
# Case 2: Allow wavelength to be missing.
# Valid q values, no wavelength, return index array
(
None,
np.array([0, 0.2, 0.4, 0.6, 0.8, 1]),
np.array([0, 1, 2, 3, 4, 5]),
),
# Case 3: Correctly specified q and wavelength
# Expected tth values are 2*arcsin(q) in degrees
(4 * np.pi, np.array([0, 1 / np.sqrt(2), 1.0]), np.array([0, 90.0, 180.0])),
],
)
Thank you @sbillinge. Above is used for one of the refactored test functions. For other test functions, I could further make improvements via new PRs. Adding more here is a bit hard for you and me to track things..
For non-UC behavior, we've decided to use the following format in the group as the group standard: