Merge pull request #2157 from bagerard/switch_pytest

Switch to pytest as runner
This commit is contained in:
Bastien Gérard 2019-10-31 22:50:39 +01:00 committed by GitHub
commit aa6ff8c84a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 123 additions and 53 deletions

View File

@ -63,8 +63,8 @@ install:
- pip install flake8 flake8-import-order - pip install flake8 flake8-import-order
- pip install tox # tox 3.11.0 has requirement virtualenv>=14.0.0 - pip install tox # tox 3.11.0 has requirement virtualenv>=14.0.0
- pip install virtualenv # virtualenv>=14.0.0 has dropped Python 3.2 support (and pypy3 is based on py32) - pip install virtualenv # virtualenv>=14.0.0 has dropped Python 3.2 support (and pypy3 is based on py32)
# Install the tox venv. # tox dryrun to setup the tox venv (we run a mock test).
- tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -e test - tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -a "-k=test_ci_placeholder"
# Install black for Python v3.7 only. # Install black for Python v3.7 only.
- if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then pip install black; fi - if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then pip install black; fi
@ -76,13 +76,13 @@ before_script:
- mongo --eval 'db.version();' # Make sure mongo is awake - mongo --eval 'db.version();' # Make sure mongo is awake
script: script:
- tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- --with-coverage - tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -a "--cov=mongoengine"
# For now only submit coveralls for Python v2.7. Python v3.x currently shows # For now only submit coveralls for Python v2.7. Python v3.x currently shows
# 0% coverage. That's caused by 'use_2to3', which builds the py3-compatible # 0% coverage. That's caused by 'use_2to3', which builds the py3-compatible
# code in a separate dir and runs tests on that. # code in a separate dir and runs tests on that.
after_success: after_success:
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then coveralls --verbose; fi - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then coveralls --verbose; else echo "coveralls only sent for py27"; fi
notifications: notifications:
irc: irc.freenode.org#mongoengine irc: irc.freenode.org#mongoengine

View File

@ -116,7 +116,8 @@ Some simple examples of what MongoEngine code looks like:
Tests Tests
===== =====
To run the test suite, ensure you are running a local instance of MongoDB on To run the test suite, ensure you are running a local instance of MongoDB on
the standard port and have ``nose`` installed. Then, run ``python setup.py nosetests``. the standard port and have ``pytest`` installed. Then, run ``python setup.py test``
or simply ``pytest``.
To run the test suite on every supported Python and PyMongo version, you can To run the test suite on every supported Python and PyMongo version, you can
use ``tox``. You'll need to make sure you have each supported Python version use ``tox``. You'll need to make sure you have each supported Python version
@ -129,16 +130,14 @@ installed in your environment and then:
# Run the test suites # Run the test suites
$ tox $ tox
If you wish to run a subset of tests, use the nosetests convention: If you wish to run a subset of tests, use the pytest convention:
.. code-block:: shell .. code-block:: shell
# Run all the tests in a particular test file # Run all the tests in a particular test file
$ python setup.py nosetests --tests tests/fields/fields.py $ pytest tests/fields/test_fields.py
# Run only particular test class in that file # Run only particular test class in that file
$ python setup.py nosetests --tests tests/fields/fields.py:FieldTest $ pytest tests/fields/test_fields.py::TestField
# Use the -s option if you want to print some debug statements or use pdb
$ python setup.py nosetests --tests tests/fields/fields.py:FieldTest -s
Community Community
========= =========

View File

@ -19,6 +19,7 @@ Development
- From now on keyword arguments (e.g. ``Doc(field_name=value)``) are required. - From now on keyword arguments (e.g. ``Doc(field_name=value)``) are required.
- Fix updating/modifying/deleting/reloading a document that's sharded by a field with ``db_field`` specified. #2125 - Fix updating/modifying/deleting/reloading a document that's sharded by a field with ``db_field`` specified. #2125
- ``ListField`` now accepts an optional ``max_length`` parameter. #2110 - ``ListField`` now accepts an optional ``max_length`` parameter. #2110
- Switch from nosetest to pytest as test runner #2114
- The codebase is now formatted using ``black``. #2109 - The codebase is now formatted using ``black``. #2109
- In bulk write insert, the detailed error message would raise in exception. - In bulk write insert, the detailed error message would raise in exception.

View File

@ -11,7 +11,8 @@
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
import sys, os import os
import sys
import sphinx_rtd_theme import sphinx_rtd_theme

View File

@ -247,8 +247,8 @@ class query_counter(object):
- self._ctx_query_counter - self._ctx_query_counter
) )
self._ctx_query_counter += ( self._ctx_query_counter += (
1 1 # Account for the query we just issued to gather the information
) # Account for the query we just issued to gather the information )
return count return count

View File

@ -1193,9 +1193,7 @@ class BaseQuerySet(object):
validate_read_preference("read_preference", read_preference) validate_read_preference("read_preference", read_preference)
queryset = self.clone() queryset = self.clone()
queryset._read_preference = read_preference queryset._read_preference = read_preference
queryset._cursor_obj = ( queryset._cursor_obj = None # we need to re-create the cursor object whenever we apply read_preference
None
) # we need to re-create the cursor object whenever we apply read_preference
return queryset return queryset
def scalar(self, *fields): def scalar(self, *fields):

View File

@ -1,11 +1,10 @@
[nosetests]
verbosity=2
detailed-errors=1
#tests=tests
cover-package=mongoengine
[flake8] [flake8]
ignore=E501,F401,F403,F405,I201,I202,W504, W605, W503 ignore=E501,F401,F403,F405,I201,I202,W504, W605, W503
exclude=build,dist,docs,venv,venv3,.tox,.eggs,tests exclude=build,dist,docs,venv,venv3,.tox,.eggs,tests
max-complexity=47 max-complexity=47
application-import-names=mongoengine,tests application-import-names=mongoengine,tests
[tool:pytest]
# Limits the discovery to tests directory
# avoids that it runs for instance the benchmark
testpaths = tests

View File

@ -1,6 +1,9 @@
import os import os
import sys import sys
from pkg_resources import normalize_path
from setuptools import find_packages, setup from setuptools import find_packages, setup
from setuptools.command.test import test as TestCommand
# Hack to silence atexit traceback in newer python versions # Hack to silence atexit traceback in newer python versions
try: try:
@ -24,6 +27,62 @@ def get_version(version_tuple):
return ".".join(map(str, version_tuple)) return ".".join(map(str, version_tuple))
class PyTest(TestCommand):
"""Will force pytest to search for tests inside the build directory
for 2to3 converted code (used by tox), instead of the current directory.
Required as long as we need 2to3
Known Limitation: https://tox.readthedocs.io/en/latest/example/pytest.html#known-issues-and-limitations
Source: https://www.hackzine.org/python-testing-with-pytest-and-2to3-plus-tox-and-travis-ci.html
"""
# https://pytest.readthedocs.io/en/2.7.3/goodpractises.html#integration-with-setuptools-test-commands
# Allows to provide pytest command argument through the test runner command `python setup.py test`
# e.g: `python setup.py test -a "-k=test"`
# This only works for 1 argument though
user_options = [("pytest-args=", "a", "Arguments to pass to py.test")]
def initialize_options(self):
TestCommand.initialize_options(self)
self.pytest_args = ""
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = ["tests"]
self.test_suite = True
def run_tests(self):
# import here, cause outside the eggs aren't loaded
from pkg_resources import _namespace_packages
import pytest
# Purge modules under test from sys.modules. The test loader will
# re-import them from the build location. Required when 2to3 is used
# with namespace packages.
if sys.version_info >= (3,) and getattr(self.distribution, "use_2to3", False):
module = self.test_args[-1].split(".")[0]
if module in _namespace_packages:
del_modules = []
if module in sys.modules:
del_modules.append(module)
module += "."
for name in sys.modules:
if name.startswith(module):
del_modules.append(name)
map(sys.modules.__delitem__, del_modules)
# Run on the build directory for 2to3-built code
# This will prevent the old 2.x code from being found
# by py.test discovery mechanism, that apparently
# ignores sys.path..
ei_cmd = self.get_finalized_command("egg_info")
self.test_args = [normalize_path(ei_cmd.egg_base)]
cmd_args = self.test_args + ([self.pytest_args] if self.pytest_args else [])
errno = pytest.main(cmd_args)
sys.exit(errno)
# Dirty hack to get version number from monogengine/__init__.py - we can't # Dirty hack to get version number from monogengine/__init__.py - we can't
# import it as it depends on PyMongo and PyMongo isn't installed until this # import it as it depends on PyMongo and PyMongo isn't installed until this
# file is read # file is read
@ -51,7 +110,13 @@ CLASSIFIERS = [
extra_opts = { extra_opts = {
"packages": find_packages(exclude=["tests", "tests.*"]), "packages": find_packages(exclude=["tests", "tests.*"]),
"tests_require": ["nose", "coverage==4.2", "blinker", "Pillow>=2.0.0"], "tests_require": [
"pytest<5.0",
"pytest-cov",
"coverage<5.0", # recent coverage switched to sqlite format for the .coverage file which isn't handled properly by coveralls
"blinker",
"Pillow>=2.0.0",
],
} }
if sys.version_info[0] == 3: if sys.version_info[0] == 3:
extra_opts["use_2to3"] = True extra_opts["use_2to3"] = True
@ -79,6 +144,6 @@ setup(
platforms=["any"], platforms=["any"],
classifiers=CLASSIFIERS, classifiers=CLASSIFIERS,
install_requires=["pymongo>=3.4", "six"], install_requires=["pymongo>=3.4", "six"],
test_suite="nose.collector", cmdclass={"test": PyTest},
**extra_opts **extra_opts
) )

View File

@ -1615,7 +1615,7 @@ class TestInstance(MongoDBTestCase):
self.assertEqual(person.active, False) self.assertEqual(person.active, False)
def test__get_changed_fields_same_ids_reference_field_does_not_enters_infinite_loop_embedded_doc( def test__get_changed_fields_same_ids_reference_field_does_not_enters_infinite_loop_embedded_doc(
self self,
): ):
# Refers to Issue #1685 # Refers to Issue #1685
class EmbeddedChildModel(EmbeddedDocument): class EmbeddedChildModel(EmbeddedDocument):
@ -1629,7 +1629,7 @@ class TestInstance(MongoDBTestCase):
self.assertEqual(changed_fields, []) self.assertEqual(changed_fields, [])
def test__get_changed_fields_same_ids_reference_field_does_not_enters_infinite_loop_different_doc( def test__get_changed_fields_same_ids_reference_field_does_not_enters_infinite_loop_different_doc(
self self,
): ):
# Refers to Issue #1685 # Refers to Issue #1685
class User(Document): class User(Document):

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import datetime import datetime
import math
import itertools import itertools
import math
import re import re
from mongoengine import * from mongoengine import *

View File

@ -1,17 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from mongoengine import ( from mongoengine import (
Document, Document,
StringField,
ValidationError,
EmbeddedDocument, EmbeddedDocument,
EmbeddedDocumentField, EmbeddedDocumentField,
InvalidQueryError,
LookUpError,
IntField,
GenericEmbeddedDocumentField, GenericEmbeddedDocumentField,
IntField,
InvalidQueryError,
ListField, ListField,
EmbeddedDocumentListField, LookUpError,
ReferenceField, StringField,
ValidationError,
) )
from tests.utils import MongoDBTestCase from tests.utils import MongoDBTestCase

View File

@ -79,7 +79,7 @@ class TestField(MongoDBTestCase):
self.assertEqual(data_to_be_saved, ["age", "created", "day", "name", "userid"]) self.assertEqual(data_to_be_saved, ["age", "created", "day", "name", "userid"])
def test_custom_field_validation_raise_deprecated_error_when_validation_return_something( def test_custom_field_validation_raise_deprecated_error_when_validation_return_something(
self self,
): ):
# Covers introduction of a breaking change in the validation parameter (0.18) # Covers introduction of a breaking change in the validation parameter (0.18)
def _not_empty(z): def _not_empty(z):
@ -202,7 +202,7 @@ class TestField(MongoDBTestCase):
self.assertEqual(data_to_be_saved, ["age", "created", "userid"]) self.assertEqual(data_to_be_saved, ["age", "created", "userid"])
def test_default_value_is_not_used_when_changing_value_to_empty_list_for_strict_doc( def test_default_value_is_not_used_when_changing_value_to_empty_list_for_strict_doc(
self self,
): ):
"""List field with default can be set to the empty list (strict)""" """List field with default can be set to the empty list (strict)"""
# Issue #1733 # Issue #1733
@ -216,7 +216,7 @@ class TestField(MongoDBTestCase):
self.assertEqual(reloaded.x, []) self.assertEqual(reloaded.x, [])
def test_default_value_is_not_used_when_changing_value_to_empty_list_for_dyn_doc( def test_default_value_is_not_used_when_changing_value_to_empty_list_for_dyn_doc(
self self,
): ):
"""List field with default can be set to the empty list (dynamic)""" """List field with default can be set to the empty list (dynamic)"""
# Issue #1733 # Issue #1733
@ -1245,7 +1245,7 @@ class TestField(MongoDBTestCase):
self.assertEqual(a.b.c.txt, "hi") self.assertEqual(a.b.c.txt, "hi")
def test_embedded_document_field_cant_reference_using_a_str_if_it_does_not_exist_yet( def test_embedded_document_field_cant_reference_using_a_str_if_it_does_not_exist_yet(
self self,
): ):
raise SkipTest( raise SkipTest(
"Using a string reference in an EmbeddedDocumentField does not work if the class isnt registerd yet" "Using a string reference in an EmbeddedDocumentField does not work if the class isnt registerd yet"

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from bson import SON, DBRef from bson import DBRef, SON
from mongoengine import * from mongoengine import *

9
tests/test_ci.py Normal file
View File

@ -0,0 +1,9 @@
def test_ci_placeholder():
# This empty test is used within the CI to
# setup the tox venv without running the test suite
# if we simply skip all test with pytest -k=wrong_pattern
# pytest command would return with exit_code=5 (i.e "no tests run")
# making travis fail
# this empty test is the recommended way to handle this
# as described in https://github.com/pytest-dev/pytest/issues/2393
pass

View File

@ -1,7 +1,7 @@
import unittest import unittest
from mongoengine.common import _import_class
from mongoengine import Document from mongoengine import Document
from mongoengine.common import _import_class
class TestCommon(unittest.TestCase): class TestCommon(unittest.TestCase):

View File

@ -168,7 +168,7 @@ class ConnectionTest(unittest.TestCase):
) )
def test_connect_fails_if_similar_connection_settings_arent_defined_the_same_way( def test_connect_fails_if_similar_connection_settings_arent_defined_the_same_way(
self self,
): ):
"""Intended to keep the detecton function simple but robust""" """Intended to keep the detecton function simple but robust"""
db_name = "mongoenginetest" db_name = "mongoenginetest"

View File

@ -3,11 +3,11 @@ import unittest
from mongoengine import * from mongoengine import *
from mongoengine.connection import get_db from mongoengine.connection import get_db
from mongoengine.context_managers import ( from mongoengine.context_managers import (
switch_db,
switch_collection,
no_sub_classes,
no_dereference, no_dereference,
no_sub_classes,
query_counter, query_counter,
switch_collection,
switch_db,
) )
from mongoengine.pymongo_support import count_documents from mongoengine.pymongo_support import count_documents

View File

@ -2,7 +2,7 @@ import unittest
from six import iterkeys from six import iterkeys
from mongoengine import Document from mongoengine import Document
from mongoengine.base.datastructures import StrictDict, BaseList, BaseDict from mongoengine.base.datastructures import BaseDict, BaseList, StrictDict
class DocumentStub(object): class DocumentStub(object):
@ -20,8 +20,8 @@ class TestBaseDict(unittest.TestCase):
fake_doc = DocumentStub() fake_doc = DocumentStub()
base_list = BaseDict(dict_items, instance=None, name="my_name") base_list = BaseDict(dict_items, instance=None, name="my_name")
base_list._instance = ( base_list._instance = (
fake_doc fake_doc # hack to inject the mock, it does not work in the constructor
) # hack to inject the mock, it does not work in the constructor )
return base_list return base_list
def test___init___(self): def test___init___(self):
@ -156,8 +156,8 @@ class TestBaseList(unittest.TestCase):
fake_doc = DocumentStub() fake_doc = DocumentStub()
base_list = BaseList(list_items, instance=None, name="my_name") base_list = BaseList(list_items, instance=None, name="my_name")
base_list._instance = ( base_list._instance = (
fake_doc fake_doc # hack to inject the mock, it does not work in the constructor
) # hack to inject the mock, it does not work in the constructor )
return base_list return base_list
def test___init___(self): def test___init___(self):

View File

@ -1,7 +1,7 @@
import unittest import unittest
from pymongo import ReadPreference
from pymongo import MongoClient from pymongo import MongoClient
from pymongo import ReadPreference
import mongoengine import mongoengine
from mongoengine.connection import ConnectionFailure from mongoengine.connection import ConnectionFailure

View File

@ -1,5 +1,5 @@
import unittest
import re import re
import unittest
from mongoengine.base.utils import LazyRegexCompiler from mongoengine.base.utils import LazyRegexCompiler

View File

@ -4,7 +4,7 @@ import unittest
from nose.plugins.skip import SkipTest from nose.plugins.skip import SkipTest
from mongoengine import connect from mongoengine import connect
from mongoengine.connection import get_db, disconnect_all from mongoengine.connection import disconnect_all, get_db
from mongoengine.mongodb_support import get_mongodb_version from mongoengine.mongodb_support import get_mongodb_version

View File

@ -3,7 +3,7 @@ envlist = {py27,py35,pypy,pypy3}-{mg34,mg36}
[testenv] [testenv]
commands = commands =
python setup.py nosetests {posargs} python setup.py test {posargs}
deps = deps =
nose nose
mg34: pymongo>=3.4,<3.5 mg34: pymongo>=3.4,<3.5