-
Notifications
You must be signed in to change notification settings - Fork 1.3k
tests: replace spy() with pytest-mock spy #3208
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
tests: replace spy() with pytest-mock spy #3208
Conversation
ghost
left a 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.
@fabiosantoscode , thanks!
I'd prefer to handle the conversion separately, for now, the use_mocker method is good enough)
tests/func/test_add.py
Outdated
| def test(self): | ||
| file_md5_counter = spy(dvc.remote.local.file_md5) | ||
| file_md5_counter = self.mocker.spy(dvc.remote.local, "file_md5") | ||
| with patch.object(dvc.remote.local, "file_md5", file_md5_counter): |
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.
With mocker spy, there is no need to patch, it is automatically done by mocker, so with statement can be removed
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.
Great to know, thanks!
tests/func/test_add.py
Outdated
| def test(self): | ||
| file_md5_counter = spy(dvc.remote.local.file_md5) | ||
| file_md5_counter = self.mocker.spy(dvc.remote.local, "file_md5") | ||
| with patch.object(dvc.remote.local, "file_md5", file_md5_counter): |
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.
As above
tests/func/test_add.py
Outdated
| get_dir_checksum_counter = self.mocker.spy( | ||
| RemoteLOCAL, "get_dir_checksum" | ||
| ) | ||
| with patch.object( |
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.
As above
tests/func/test_add.py
Outdated
| stage_creator_spy = spy(dvc.repo.add._create_stages) | ||
| stage_creator_spy = self.mocker.spy(dvc.repo.add, "_create_stages") | ||
|
|
||
| with patch.object(dvc.repo.add, "_create_stages", stage_creator_spy): |
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.
As above
tests/func/test_add.py
Outdated
|
|
||
| unprotect_spy = spy(RemoteLOCAL.unprotect) | ||
| unprotect_spy = mocker.spy(RemoteLOCAL, "unprotect") | ||
| mocker.patch.object(RemoteLOCAL, "unprotect", unprotect_spy) |
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.
As above
tests/func/test_add.py
Outdated
|
|
||
| copy_spy = spy(dvc.cache.local.copy) | ||
| copy_spy = mocker.spy(dvc.cache.local, "copy") | ||
| mocker.patch.object(dvc.cache.local, "copy", copy_spy) |
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.
as above
tests/func/test_data_cloud.py
Outdated
| test_get_file_checksum = self.mocker.spy( | ||
| RemoteLOCAL, "get_file_checksum" | ||
| ) | ||
| with patch.object( |
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.
as above
tests/func/test_import_url.py
Outdated
| remove_outs_call_counter = self.mocker.spy( | ||
| dvc.stage.Stage, "remove_outs" | ||
| ) | ||
| with patch.object( |
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.
as above
tests/unit/utils/test_fs.py
Outdated
| contains_symlink_spy = self.mocker.spy( | ||
| dvc.utils.fs, "contains_symlink_up_to" | ||
| ) | ||
| with patch.object( |
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.
as above
|
Removed the useless patch.object(), thanks @pared |
|
@fabiosantoscode, moving to mocker's spy seems good. But to be honest I don't like that we are mixning |
|
Sounds good @pared let me get to that. |
|
@pared there ⛄ |
tests/func/test_add.py
Outdated
| from tests.utils import get_gitignore_content | ||
| from tests.utils import spy | ||
|
|
||
| dvc_module = dvc # to disambiguate between the module and the 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.
maybe import dvc as dvc_module?
tests/func/test_add.py
Outdated
| with patch.object(dvc.remote.local, "file_md5", file_md5_counter): | ||
| ret = main(["config", "cache.type", "copy"]) | ||
| self.assertEqual(ret, 0) | ||
| class TestDvcWithMocker(TestDvc): |
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.
Seems like it can be removed now :)
| self.assertEqual(file_md5_counter.mock.call_count, 1) | ||
| ret = main(["add", "foo"]) | ||
| assert ret == 0 | ||
| assert file_md5_counter.mock.call_count == 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.
can be simplified to file_md5_counter.call_count, this applies to each use case of spy
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.
@pared It is a direct conversion that is explicit, we could leave it as is with == 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.
Should I change it to not have .mock then? It does seem to do the same thing.
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.
It does, I don't think its crucial, let's leave it as is.
| tmp_dir.gen({"foo": "foo"}) | ||
| test_get_file_checksum = mocker.spy(RemoteLOCAL, "get_file_checksum") | ||
| url = Local.get_url() | ||
| ret = main(["remote", "add", "-d", TEST_REMOTE, url]) |
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 need to use TEST_REMOTE, you can use some string name, like "upstream"
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.
@pared I think it is out of scope for this change 🙂
tests/func/test_import_url.py
Outdated
| self.assertEqual(fd.read(), "content") | ||
|
|
||
|
|
||
| class TestShouldRemoveOutsBeforeImport(TestDvc): |
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 can also be refactored.
prepare external resource using tmp_path_factory fixture (eg tmp_path_factory.mktemp("external_source") / "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.
I copied this before refactoring, then forgot to remove the original :)
tests/func/test_import_url.py
Outdated
|
|
||
|
|
||
| def test_should_remove_outs_before_import(mocker, dvc, tmp_path_factory): | ||
| import_dir = TmpDir(tmp_path_factory.mktemp("import")) |
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 have erepo_dir fixture for such things:
with erepo_dir.chdir(): erepo_dir.dvc_gen("foo": "foo content")
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.
Also, this test does not pass for py3.5, need to wrap tmp_path_factory.mktemp in fspath.
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.
Makes sense.
tests/unit/utils/test_fs.py
Outdated
| dirname_patch.assert_called_once() | ||
|
|
||
| @patch.object(System, "is_symlink", return_value=False) | ||
| def test_should_call_recursive_on_no_condition_matched(self, _): |
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 test can also be refactored. Just extract it to standalone test, not part of TestContainsLink class.
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.
Done!
|
I've fixed most comments @pared and @efiop except this one: #3208 (comment) about which I'm not clear on what to do. |
pared
left a 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.
Good job @fabiosantoscode! LGTM
After I'd started, @pared pointed out that it would be best to convert the tests to plain functions as opposed to classes, to better utilise the
mockerfixture. I ended up having to create a bunch of wrappers just to use this fixture. Let me know if you'd like me to go ahead and convert all of the classes that were using spy into plain pytest function tests.Fixes #3198
❗ Have you followed the guidelines in the Contributing to DVC list?
📖 Check this box if this PR does not require documentation updates, or if it does and you have created a separate PR in dvc.org with such updates (or at least opened an issue about it in that repo). Please link below to your PR (or issue) in the dvc.org repo.
❌ Have you checked DeepSource, CodeClimate, and other sanity checks below? We consider their findings recommendatory and don't expect everything to be addressed. Please review them carefully and fix those that actually improve code or fix bugs.
Thank you for the contribution - we'll try to review it as soon as possible. 🙏