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
[MAINT] Refactor fixtures #3905
Conversation
👋 @Remi-Gau Thanks for creating a PR! Until this PR is ready for review, you can include the [WIP] tag in its title, or leave it as a github draft. Please make sure it is compliant with our contributing guidelines. In particular, be sure it checks the boxes listed below.
For new features:
For bug fixes:
We will review it as quick as possible, feel free to ping us with questions if needed. |
oops... introduced some test pollution |
Codecov Report
@@ Coverage Diff @@
## main #3905 +/- ##
=======================================
Coverage 91.76% 91.76%
=======================================
Files 134 134
Lines 15747 15747
Branches 3283 3283
=======================================
Hits 14451 14451
Misses 752 752
Partials 544 544
Flags with carried forward coverage won't be shown. Click here to find out more. 📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
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.
@Remi-Gau why is it that for mni affine the fixture returns the AFFINE_MNI
array itself whereas with the eye affine the fixture returns a function that returns AFFINE_EYE
array? Same question for shape_3d_default
vs shape_4d_default
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.
General answer.
I went for
# global variable
X = "some value"
# function
def _x()
return X
# fixture
@pytest.mark.fixture()
def x():
return _x()
When some other fixtures needed to access the value of X and change it.
So to avoid having that fixture change the global (AKA "best way to make things break") I added a function as a go between.
I have not done that systematically because I have not had the need for it for all of those constants. But I think that it would make sense to have the same pattern for all, to avoid this confusion.
@Remi-Gau thanks for the explanations. I don't necessarily think you have to change the way you did things if you have a good reason but a comment or docstrings for the helper functions and fixtures might be sufficient. And this could take a bit of extra work to get a good "base" for fixtures that we can then iterate on but I think it will improve testing quite a bit so thanks! |
Good point will add some comments and doc strings. |
@@ -89,7 +94,114 @@ def close_all(): | |||
) | |||
|
|||
|
|||
def _mni_3d_img(affine=MNI_AFFINE): | |||
def _affine_mni(): |
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.
Does anything prevent from always using this even inside conftest? What is the benefit of still having the global?
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.
yeah I was thinking that the global was progressively becoming useless.
will try to simplify.
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 actually removed all constants.
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.
But isn't that a breaking change ? I'd rather have deprecation cycles...
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.
looking through all the files changed, all those constants only appear in tests so I don't think deprecation is necessary there.
are we actually storing anything in conftest.py that is not just for testing? if so we should probably move it somewhere else
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.
LGTM. Simply woried about API breakage in conftest.py
@@ -89,7 +94,114 @@ def close_all(): | |||
) | |||
|
|||
|
|||
def _mni_3d_img(affine=MNI_AFFINE): | |||
def _affine_mni(): |
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.
But isn't that a breaking change ? I'd rather have deprecation cycles...
I was thinking of adding a bit of contributing doc regarding the use of fixtures: should I do it in this PR? It feels more appropriate but it may make the PR bigger. |
I was just thinking that some extra package may rely on it ... |
Should be in this PR I think. |
except pytest plugins I think that no other package would expect its config information to go in there (at least I kind of hope so) |
In addition the changes in conftest.py have not been released in a stable version yet |
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.
Updated the doc on the tests to talk about fixtures and improve this section in general with a couple of extra links.
(for example you need to generate some data, or a NiftiImage object or a file...) | ||
you can use `pytest fixtures <https://docs.pytest.org/en/6.2.x/fixture.html>`_ | ||
to help you mock this data | ||
(more information on pytest fixtures in `this video <https://www.youtube.com/watch?v=ScEQRKwUePI>`_). | ||
|
||
Fixture are recognizable because they have a `@pytest.fixture` decorator. | ||
Fixtures that are shared by many tests modules can be found in `nilearn/conftest.py` | ||
but some fixures specific to certain modules can also be kept in that testing module. |
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.
maybe we can be more explicit in instructing to first see if any of the available fixtures can be used before writing a new one
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.
added one sentence to make it explicit.
CONTRIBUTING.rst
Outdated
Tests must be seeded to avoid random failures. | ||
For objects using random seeds (e.g. scikit-learn estimators), pass either | ||
a `np.random.RandomState` or an `int` as the seed. | ||
When your test use random numbers, those must be generated through: | ||
a `np.random.RandomState` or an `int` as the seed. |
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.
Maybe for a separate PR but shouldn't we update to use the Generator API as mentioned in #3331 (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.
I updated it here already: what do you think?
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, good with me! At some point we can update our tests to use it but that really should be in a separate PR
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.
agreed
I will take care of it soon but it should be done in a separate or several PRs
Co-authored-by: Yasmin <63292494+ymzayek@users.noreply.github.com>
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.
Thx @Remi-Gau !
Changes proposed in this pull request:
In nilearn/conftest.py adds:
Apply those changes in the tests that have already been refactored