10000 [MRG+2] Pytest parametrize unit tests by rth · Pull Request #11074 · scikit-learn/scikit-learn · GitHub
[go: up one dir, main page]

Skip to content

[MRG+2] Pytest parametrize unit tests #11074

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

Merged
merged 33 commits into from
Jun 8, 2018

Conversation

rth
Copy link
Member
@rth rth commented May 7, 2018

Reference Issues/PRs

Continues #8321

Fixes #10728, related to #7319 , #11063

What does this implement/fix? Explain your changes.

This PR,

  1. Replaces tests that use yield with pytest parameterization
  2. Parametrizing some tests that don't use yield but where it would be helpful (e.g. a tests is a for loop over estimators, solvers etc). This applies to,
    def test_a():
       for solver in solvers:
           # run some tests
    but not
    def test_b():
       # computationally expensive setup + some tests
       for solver in solvers:
           # run some tests
  3. In some occasions, where using paramerization is not straightforward, yields were simply removed i.e. yield func, arg replaced by func(arg)

Beyond removing deprecation warnings in pytest, the motivation is somewhat similar to #11063 : better verbosity in tests logs and ability to select tests with the -k argument.

Any other comments?

The changes are fairly mechanical but there is a lot of them. For review, I think it would make sense to split this in smaller pieces,

Please add general comments here, but review specific PRs linked above. The diff in this PR will be updated in the future. Once the above PRs are merged, the remaining diff can be directly reviewed here.

Because a lot of these changes consist in taking a loop over some list of parameters, and writing it with @pytest.mark.parametrize the diff contains a lot of lines where only the indentation changes. As a workaround, I find helpful the option to ignore whitespaces in Github diff (by appending the &w=1 string to the URL or using the Refined Github extension which creates a UI button for it).

Please let met me know if can do anything to make the review easier.

@amueller
Copy link
Member

still broken?

@rth
Copy link
Member Author
8000 rth commented May 20, 2018

still broken?

Was waiting for #11075 to reach consensus before updating this PR with changes there, which should also help fixing CI..

@rth rth force-pushed the pytest-parametrize-non-common-tests branch from 554902b to fbfb976 Compare May 24, 2018 08:02
@rth
Copy link
Member Author
rth commented May 27, 2018

Tests are passing now, but it still might be easier review smaller chunks of this linked in the second part of the description #11074 (comment) (or liked above this comment).

Leaving the WIP status even if this PR is mostly done to indicate that the diff will be updated as its chunks / sub-PRs are merged.

@qinhanmin2014
Copy link
Member

Curious about the purpose of this PR? If it contains all the remaining things except from part1-part3 (after part3 begin merged), I think it might be better to review this PR directly (at least I'm willing to do so)

@rth rth changed the title [WIP] Pytest parametrize unit tests [MRG] Pytest parametrize unit tests Jun 1, 2018
@rth
Copy link
Member Author
rth commented Jun 1, 2018

Initially, I was trying to split it in smaller peaces to make review easier. After part 3 (#11143) is merged, I agree that the remaining diff here should be managable. Marking this as MRG. Thanks @qinhanmin2014 !

Copy link
Member
@lesteve lesteve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just a small side-comment about the instable test that you found.

@@ -153,6 +156,8 @@ def test_ridge_regression_convergence_fail():

def test_ridge_sample_weights():
# TODO: loop over sparse data as well
# Note: parametrizing this test with pytest results in failed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, maybe you can create an issue about this, if you haven't already.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #11200

@lesteve lesteve changed the title [MRG] Pytest parametrize unit tests [MRG+1] Pytest parametrize unit tests Jun 4, 2018
@qinhanmin2014
Copy link
Member

@rth I've merged part3 and will give this a review these days. I think it's important to quickly get this in to avoid conflicts.
Couple of things:
(1) Need to merge master in.
(2) Could you please share how you did this? I think it's important for the completeness of the PR.

@lesteve
Copy link
Member
lesteve commented Jun 4, 2018

I have to admit I have not followed closely the subdivision of this work. Ping me if you need another review to get this completed.

@rth
Copy link
Member Author
rth commented Jun 4, 2018

Thanks for the review @lesteve - will open the issue!

@qinhanmin2014 I realized there was a PR duplication issue, but haven't though about it in terms or PR approval transitivity :) Thanks, everything should be in order now, this is the only PR for this parametrization, that remains.

Could you please share how you did this? I think it's important for the completeness of the PR.

You mean how I arrived at the diff in this PR? Well, some was already done in #8321 I then searched for tests using yield with the command from #10728 (comment) . A more detailed list of occurrences (as opposed to files) can be obtained on Linux with,

find sklearn -iname "*test_*py" -type f -exec grep -H --color -i yield {} \;

Then I realized that there were a lot of tests that would benefit from straightforward parametrization, so I manually went through tests - file by file and made the changes.

Copy link
Member
@qinhanmin2014 qinhanmin2014 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of questions, LGTM.

3420
@@ -126,7 +123,7 @@ def test_normalized_output(metric_name):
# that is when 0 and 1 exchanged.
@pytest.mark.parametrize(
"metric_name",
[name for name in dict(SUPERVISED_METRICS, **UNSUPERVISED_METRICS)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someone has pointed out that dict(x, **y) is not recommended in part1. But I don't think we're going to modify parametrized tests too much, so LGTM.


assert_raises(ValueError, mixture.GMM, n_components=20,
covariance_type='badcovariance_type')
with pytest.raises(ValueError):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why getting rid of assert_raises here? I can't find any explanation. Also, I think there are many other places using assert_raises. Maybe revert it and open an issue if needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right - better be systematic. Reverted this.

def test_1d_input():
for name in ALL_TREES:
yield check_raise_error_on_1d_input, name
# XXX
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's happening here for the XXX?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was probably a note in the making, don't remember now - possibly related to #11109 about incompatibility of ignore_warnings with parametrization. Thanks for catching this.

Removed it, and used ignore_warnings as a context manager. Hopefully this will be fixed in #11109

@qinhanmin2014
Copy link
Member

Hmm, @rth I still get these in Appveyor. I think they're valid.

sklearn/metrics/tests/test_common.py::test_single_sample
  yield tests are deprecated, and scheduled to be removed in pytest 4.0
sklearn/metrics/tests/test_common.py::test_averaging_multiclass
  yield tests are deprecated, and scheduled to be removed in pytest 4.0
sklearn/metrics/tests/test_common.py::test_averaging_multilabel
  yield tests are deprecated, and scheduled to be removed in pytest 4.0
sklearn/metrics/tests/test_common.py::test_averaging_multilabel_all_zeroes
  yield tests are deprecated, and scheduled to be removed in pytest 4.0
sklearn/metrics/tests/test_common.py::test_averaging_multilabel_all_ones
  yield tests are deprecated, and scheduled to be removed in pytest 4.0
sklearn/metrics/tests/test_common.py::test_sample_weight_invariance
  yield tests are deprecated, and scheduled to be removed in pytest 4.0

I won't be strict about the completeness of parametrization, but I think we should remove all the yield tests here.

@rth
Copy link
Member Author
rth commented Jun 5, 2018

Thanks for the detailed review @qinhanmin2014 !

but I think we should remove all the yield tests here.

You are right, looks like I missed parametrizations in sklearn/metrics/tests/test_common.py file. Will add it tomorrow.

@rth
Copy link
Member Author
rth commented Jun 6, 2018

I added the missing parametrizations in dd89184 for metrics @qinhanmin2014 . Had to split a few tests to remove all yields.

Copy link
Member
@qinhanmin2014 qinhanmin2014 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping @lesteve to double check your +1 since the new diff is large.

y_true, y_pred,
beta=beta, average=average)
assert_almost_equal(fbeta, 0)
p, r, f, s = assert_warns(UndefinedMetricWarning,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems a bit awkward since this part will run multiple times. I'd rather have another test here (e.g.,test_precision_recall_f1_no_labels_average_none), or some better ways to avoid running same tests for multiple times.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point done.

'name',
(set(ALL_METRICS) - set(REGRESSION_METRICS)
- set(METRICS_WITHOUT_SAMPLE_WEIGHT)
- METRIC_UNDEFINED_BINARY_MULTICLASS))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding set to keep consistent? I'm fine with current version though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a bit awkward because ALL_METRICS and REGRESSION_METRICS are dicts, while METRICS_WITHOUT_SAMPLE_WEIGHT and all the rest are lists, and finally METRIC_UNDEFINED_BINARY_MULTICLASS is a set.

I changed the ones that were lists to sets which removes the need to cast them to set in parametrizations (since we are doing a number of set operations here).

@rth
Copy link
Member Author
rth commented Jun 7, 2018

@lesteve could you please have a look to the last 2 commits? Thanks!

Copy link
Member
@qinhanmin2014 qinhanmin2014 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, ping @lesteve

@qinhanmin2014 qinhanmin2014 changed the title [MRG+1] Pytest parametrize unit tests [MRG+2] Pytest parametrize unit tests Jun 7, 2018
Copy link
Member
@lesteve lesteve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, merging! Thanks a lot for working on this, this is really appreciated!

if name in METRIC_UNDEFINED_BINARY_MULTICLASS:
continue

with ignore_warnings():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, did you find a problem with the interaction of ignore_warnings and parametrize? I bumped into some quirks as I indicated in #11151 (comment).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think same things as what you mentioned in your comment, if I recall correctly,

@lesteve lesteve merged commit e8d8b8e into scikit-learn:master Jun 8, 2018
@rth rth deleted the pytest-parametrize-non-common-tests branch June 8, 2018 15:37
@rth
Copy link
Member Author
rth commented Jun 9, 2018

Thanks for the review @qinhanmin2014 and @lesteve ! In this case reviewing was probably more work than making the changes :)

@HolgerPeters
Copy link
Contributor

@rth thanks for picking up my branch, if I had known someone wanted to work with it I would have left it in a better state :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

yield tests are deprecated in pytest
5 participants
0