diff --git a/app_helper/base_test.py b/app_helper/base_test.py index d371384..8060a77 100644 --- a/app_helper/base_test.py +++ b/app_helper/base_test.py @@ -76,9 +76,40 @@ class BaseTestCaseMixin: @classmethod def setUpClass(cls): + cls._setup_utils() + cls._setup_users() + super().setUpClass() + + @classmethod + def _setup_utils(cls): + """ + Setup generic utils as class attributes. + + * :py:attr:`request_factory`: instance of :py:class:`RequestFactory` + * :py:attr:`site_1`: instance of the first ``Django`` :py:class:`Site` + * :py:attr:`languages`: list of configured languages + """ from django.contrib.sites.models import Site cls.request_factory = RequestFactory() + cls.site_1 = Site.objects.all().first() + + try: + from cms.utils import get_language_list + + cls.languages = get_language_list() + except ImportError: + cls.languages = [x[0] for x in settings.LANGUAGES] + + @classmethod + def _setup_users(cls): + """ + Create standard users. + + * :py:attr:`user`: superuser + * :py:attr:`user_staff`: staff user + * :py:attr:`user_normal`: plain django user + """ cls.user = create_user( cls._admin_user_username, cls._admin_user_email, @@ -96,21 +127,17 @@ def setUpClass(cls): cls.user_normal = create_user( cls._user_user_username, cls._user_user_email, cls._user_user_password, is_staff=False, is_superuser=False, ) - cls.site_1 = Site.objects.all().first() - try: - from cms.utils import get_language_list - - cls.languages = get_language_list() - except ImportError: - cls.languages = [x[0] for x in settings.LANGUAGES] - super().setUpClass() + @classmethod + def _teardown_users(cls): + """Delete existing users.""" + User = get_user_model() # NOQA + User.objects.all().delete() @classmethod def tearDownClass(cls): super().tearDownClass() - User = get_user_model() # NOQA - User.objects.all().delete() + cls._teardown_users() @contextmanager def temp_dir(self): diff --git a/app_helper/runner.py b/app_helper/runner.py index 67cb810..36addea 100644 --- a/app_helper/runner.py +++ b/app_helper/runner.py @@ -59,13 +59,25 @@ def setup(app, helper_module, extra_args=None, use_cms=False): :param use_cms: setup a django CMS environemtn :return: Django settings module """ + + def _pytest_setup(settings, module): + for setting in dir(settings): + if setting.isupper(): + setting_value = getattr(settings, setting) + if setting == "SECRET_KEY" and not setting_value: + setting_value = "SECRET" + setattr(module, setting, setting_value) + helper = helper_module.__file__ argv = [os.path.basename(helper), app, "setup", "--extra-settings={}".format(helper)] if use_cms: argv.append("--cms") if extra_args: argv.extend(extra_args) - return runner(argv) + settings = runner(argv) + if "pytest_django" in sys.modules: + _pytest_setup(settings, helper_module) + return settings def runner(argv): diff --git a/changes/167.feature b/changes/167.feature new file mode 100644 index 0000000..551aae5 --- /dev/null +++ b/changes/167.feature @@ -0,0 +1 @@ +Add support to pytest command diff --git a/docs/basetest.rst b/docs/basetest.rst index 6cb560e..1a41d44 100644 --- a/docs/basetest.rst +++ b/docs/basetest.rst @@ -13,6 +13,10 @@ repetitive tasks during development. :members: :private-members: + .. automethod:: app_helper.base_test.BaseTestCase._setup_users + .. automethod:: app_helper.base_test.BaseTestCase._teardown_users + .. automethod:: app_helper.base_test.BaseTestCase._setup_utils + .. autoattribute:: app_helper.base_test.BaseTestCase._admin_user_username .. autoattribute:: app_helper.base_test.BaseTestCase._admin_user_password .. autoattribute:: app_helper.base_test.BaseTestCase._admin_user_email diff --git a/docs/pytest.rst b/docs/pytest.rst index 926961f..ef08eef 100644 --- a/docs/pytest.rst +++ b/docs/pytest.rst @@ -2,13 +2,15 @@ pytest support ############## -While its :py:class:`BaseTestCaseMixin` is built on Django ``TestCase``, -``django-app-helper`` can be used with pytest-based tests by using the provided -compatible runner as documented on `pytest-django`_ documentation. +While django-app-helper was born with Django ``TestCase`` in mind, it can be used with ``pytest`` with some configuration +together with ``pytest-django``. -To enable pytest compatible runner: +************************ +django-app-helper runner +************************ -* Add to project ``helper.py`` file: +You can run pytest tests by using a custom runner (based on `pytest-django`_ documentation); to enable it, +add the following to project ``helper.py`` file: .. code-block:: python @@ -18,12 +20,15 @@ To enable pytest compatible runner: ... } -* Run tests as usual:: +Using this approach you can mix pytest tests and Django ``TestCase`` ones, the runner will take care +of discovering and running both. - $ python helper.py test +Running tests +============== -You can also mix pytest tests and Django ``TestCase`` ones, the runner will take care -of discovering and running both. +Invoke ``app_helper`` as usual:: + + $ python helper.py test pytest options ============== @@ -51,3 +56,38 @@ In case arguments are passed via both channels they are merged together, with ru over environment variables in case of overlapping options. .. _pytest-django: https://pytest-django.readthedocs.io/en/latest/faq.html#how-can-i-use-manage-py-test-with-pytest-django + +*************** +standard pytest +*************** + +Running tests +============== + +Invoke ``pytest`` as usual:: + + $ python -mpytest + +or:: + + $ pytest + +In this case you don't need any special syntax to pass commands as the +django-app-helper pytest runner is not executed and pytest is full in control. + +.. warning: the ``pytest`` invocation will only works if you add the current directory in the ``PYTHONPATH``, thus the + ``python -mpytest`` version is preferred. + +Using BaseTestCaseMixin +======================= + +While its :py:class:`~app_helper.base_test.BaseTestCaseMixin` is built on Django ``TestCase``, it can be used in pytest classes: + +Fixtures, markers and decorators can be used as usual on test methods as in classic pytest classes. + +.. code-block:: python + + class TestTags(BaseTestCaseMixin): + ... + def test_foo(self): + ...