-
-
Notifications
You must be signed in to change notification settings - Fork 218
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
Replace nose with pytest #34778
Replace nose with pytest #34778
Conversation
cf75dbf
to
e474bf5
Compare
a0fb589
to
ed40b81
Compare
13ba100
to
73c0be9
Compare
73c0be9
to
3c62ebf
Compare
9bc121b
to
b71a55c
Compare
Required by --strict-markers
test_user_types tests were not being run because the "yield" tests were masked by @patch_user_data_db_layer, which converted the generator to a normal function, which was then "run" as a test but didn't actually test anything.
A few tests are also now found that nose was missing.
- Disable cacheprovider plugin since it is not possible to re-run failed tests in that context. - Add option to show local variables in test failure tracebacks.
Tried using capsys, but couldn't make it work.
Also a few other non-test files.
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.
(Partial review with Matt. To be continued.)
# pytest is pinned to a git commit until 8.4 is released. | ||
# `minversion = 8.4` should also be set in .pytest.ini at that time. | ||
pytest @ git+https://github.com/pytest-dev/pytest.git@85760bff2776989b365167c7aeb35c86308ab76b |
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.
What feature are we using that will be in 8.4?
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.
@@ -40,10 +43,9 @@ def main(): | |||
run_patches() | |||
|
|||
from corehq.warnings import configure_warnings | |||
configure_warnings(is_testing(sys.argv)) | |||
configure_warnings() |
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 configure_warnings() take the CCHQ_TESTING env var into account?
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 question. Should a management command (that is not running tests) fail because of warnings if CCHQ_TESTING
is set? For example, if you are running CCHQ_TESTING=1 ./manage.py migrate
to migrate your test database, should it crash if warnings are emitted? I think that might be more annoying than helpful, but I'm happy to hear arguments in favor.
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 reckoned that CCHQ_TESTING=1 ./manage.py dbshell
would useful for inspecting the test database.
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.
Are you saying you agree that that command should not crash on a warning? That's my position.
|
||
def patch_assertItemsEqual(): | ||
import unittest | ||
unittest.TestCase.assertItemsEqual = unittest.TestCase.assertCountEqual |
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.
(Why do we currently do this? Do we not test the values of the items?)
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.
assertItemsEqual
was removed, possibly as long ago as when we upgraded to Python 3, but we still have code that uses it. This was easier than changing all the places that use it. It's not new with this PR, it was moved from here.
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.
Interesting. Yeah, it looks like Python renamed assertItemsEqual
to assertCountEqual
in Python 3.2.
@@ -51,7 +52,7 @@ def simulate_reentrant_lock(): | |||
@timelimit(0.1) | |||
def test_unreleased_lock(): | |||
msg = "unreleased dict_values([ReentrantTestLock(name='unreleased', level=1)])" | |||
with assert_raises(AssertionError, msg=msg): | |||
with assert_raises(AssertionError, msg=re.compile("^" + re.escape(msg))): |
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.
Is there a reason why msg
can now be longer, or does this fix something that is currently wrong?
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 became necessary because pytest changes the assertion error message, I think as part of its assertion introspection logic:
> assert not locks, f"unreleased {locks.values()}"
E AssertionError: unreleased dict_values([ReentrantTestLock(name='unreleased', level=1)])
E assert not {'unreleased': ReentrantTestLock(name='unreleased', level=1)}
corehq/tests/locks.py:58: AssertionError
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
... snip
> self.gen.throw(typ, value, traceback)
E AssertionError: not equal
E "unreleased dict_values([ReentrantTestLock(name='unreleased', level=1)])\nassert not {'unreleased': ReentrantTestLock(name='unreleased', level=1)}"
E "unreleased dict_values([ReentrantTestLock(name='unreleased', level=1)])"
../../.pyenv/versions/3.9.17/lib/python3.9/contextlib.py:137: AssertionError
from corehq.apps.es.client import manager | ||
|
||
|
||
class ElasticSnitchPlugin(Plugin): |
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.
@millerdev curious...but is something like this still useful/needed? Do we have a backlog to track re-implementing some of these plugins with pytest
? I know the commit message says out of scope, but this one in particular seems like it was added fairly recently with the ES migration work (2023) by Joel.
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.
My thought was we can reimplement this if/when we need it. I doubt anyone has used it since Joel finished his ES work, but I'd be happy to port it to pytest if someone wants it. Do you 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.
Paired with Daniel to review this. Left a couple of non-blocking questions. Great job @millerdev 🎆
@@ -58,40 +58,8 @@ | |||
'testapps.test_pillowtop', | |||
) + tuple(INSTALLED_APPS) # noqa: F405 | |||
|
|||
TEST_RUNNER = 'django_nose.BasicNoseRunner' | |||
NOSE_ARGS = [ | |||
#'--no-migrations' # trim ~120s from test run with db 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.
Should look to see if test run time increases by ~120s after switching to pytest.
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 the answer is possibly "yes." The average build time does appear to have increased from 14 minutes to 16 minutes soon after Nov 11 when this PR was merged.
with pytest.raises(AttributeError, match="Mock object has no attribute 'info'"): | ||
CouchModel.get_db().info |
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 haven't looked closely, but I assume there is no way to get this to raise a RuntimeError similar to the other db access attempts?
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.
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.
Ha in hindsight I think this was a silly comment for me to make. I was imagining a way to raise a RuntimeError
in how CouchModel was invoked, not necessarily just having the mock translate an AttributeError into a RuntimeError for the sake of it. I'll leave on a comment on the PR as well.
config.reuse_db = reusedb = config.getoption("--reusedb") | ||
config.skip_setup_for_reuse_db = reusedb and reusedb != "reset" | ||
config.reuse_db = reusedb = config.getoption("--reusedb") or config.getvalue("reuse_db") | ||
config.skip_setup_for_reuse_db = reusedb and (reusedb != "reset" or config.getvalue("create_db")) |
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'd expect that if create_db
were true, we wouldn't skip setup?
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 docs for --create-db
say:
Re-create the database, even if it exists. This option can be used to override --reuse-db.
It will not skip setup if create_db
is true unless reusedb is not false.
Another way to say that is create_db
has no effect unless reusedb
is true. Databases are always (re)created unless reusedb
is true.
https://dimagi.atlassian.net/browse/SAAS-14431
🐡 Review by commit.
Safety Assurance
Safety story
Changes tests and documentation with a couple minor exceptions:
Automated test coverage
Absolutely.
Rollback instructions