mirror of
				https://github.com/Alexander-D-Karpov/akarpov
				synced 2025-10-31 17:57:25 +03:00 
			
		
		
		
	added better test fixtures
This commit is contained in:
		
							parent
							
								
									1fc003636b
								
							
						
					
					
						commit
						b59c8fbf8b
					
				|  | @ -1,14 +1,122 @@ | |||
| import pytest | ||||
| import io | ||||
| import logging | ||||
| import re | ||||
| 
 | ||||
| from akarpov.users.models import User | ||||
| from akarpov.users.tests.factories import UserFactory | ||||
| import factory | ||||
| import pytest | ||||
| from django.core.files.uploadedfile import SimpleUploadedFile | ||||
| from pytest_django.lazy_django import skip_if_no_django | ||||
| from rest_framework.test import APIClient | ||||
| 
 | ||||
| from akarpov.utils.config import build_redis_uri | ||||
| from akarpov.utils.faker import configure_factory_faker, configure_faker | ||||
| from akarpov.utils.pytest_factoryboy import autodiscover_factories | ||||
| 
 | ||||
| configure_factory_faker(factory.Faker) | ||||
| 
 | ||||
| autodiscover_factories() | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture(autouse=True) | ||||
| def media_storage(settings, tmpdir): | ||||
|     settings.MEDIA_ROOT = tmpdir.strpath | ||||
| def activate_django_db(db): | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture(scope="session", autouse=True) | ||||
| def update_config(): | ||||
|     from django.conf import settings | ||||
| 
 | ||||
|     settings.DEBUG = True | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture | ||||
| def user(db) -> User: | ||||
|     return UserFactory() | ||||
| def api_client(): | ||||
|     return APIClient() | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture | ||||
| def admin_client(client, user_factory): | ||||
|     admin_user = user_factory(is_superuser=True, is_staff=True) | ||||
|     client.force_login(admin_user) | ||||
|     return client | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture | ||||
| def api_user_client(api_client, user): | ||||
|     api_client.force_authenticate(user) | ||||
|     return api_client | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture | ||||
| def image_factory(): | ||||
|     def factory(filename="test.jpg", **params): | ||||
|         from PIL import Image | ||||
| 
 | ||||
|         width = params.get("width", 520) | ||||
|         height = params.get("height", width) | ||||
|         color = params.get("color", "blue") | ||||
|         image_format = params.get("format", "JPEG") | ||||
|         image_palette = params.get("palette", "RGB") | ||||
| 
 | ||||
|         thumb_io = io.BytesIO() | ||||
|         with Image.new(image_palette, (width, height), color) as thumb: | ||||
|             thumb.save(thumb_io, format=image_format) | ||||
|         return SimpleUploadedFile(filename, thumb_io.getvalue()) | ||||
| 
 | ||||
|     return factory | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture | ||||
| def image(image_factory): | ||||
|     return image_factory() | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture | ||||
| def uploaded_photo(image): | ||||
|     return SimpleUploadedFile("test.png", image.read()) | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture | ||||
| def plain_file(): | ||||
|     return io.BytesIO(b"plain_text") | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture(scope="session", autouse=True) | ||||
| def faker_session_locale(): | ||||
|     return ["en"] | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture(autouse=True) | ||||
| def add_faker_providers(faker): | ||||
|     configure_faker(faker) | ||||
|     return faker | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture(scope="session", autouse=True) | ||||
| def set_test_redis_databases(request): | ||||
|     from django.conf import settings | ||||
| 
 | ||||
|     skip_if_no_django() | ||||
|     xdist_worker = getattr(request.config, "workerinput", {}).get("workerid") | ||||
|     if xdist_worker is None: | ||||
|         return | ||||
|     worker_number_search = re.search(r"\d+", xdist_worker) | ||||
|     if not worker_number_search: | ||||
|         return | ||||
|     max_db_number = worker_number_search[0] | ||||
|     max_db_number = int(max_db_number) * 3 + 2 | ||||
|     channels_redis_url = build_redis_uri( | ||||
|         settings.CHANNELS_REDIS_HOST, | ||||
|         settings.CHANNELS_REDIS_PORT, | ||||
|         settings.CHANNELS_REDIS_USER, | ||||
|         settings.CHANNELS_REDIS_PASSWORD, | ||||
|         max_db_number, | ||||
|     ) | ||||
|     settings.CHANNEL_LAYERS["default"]["CONFIG"]["hosts"][0][ | ||||
|         "address" | ||||
|     ] = channels_redis_url | ||||
|     settings.CHANNELS_REDIS_DB = max_db_number | ||||
|     settings.CLICKHOUSE_REDIS_CONFIG["db"] = max_db_number - 1 | ||||
|     settings.CELERY_REDIS_DB = max_db_number - 2 | ||||
|  |  | |||
|  | @ -1,45 +1,20 @@ | |||
| from collections.abc import Sequence | ||||
| from typing import Any | ||||
| 
 | ||||
| from django.contrib.auth import get_user_model | ||||
| from factory import Faker, post_generation | ||||
| import factory.fuzzy | ||||
| from factory.django import DjangoModelFactory | ||||
| 
 | ||||
| from akarpov.utils.faker import django_image | ||||
| from akarpov.utils.pytest_factoryboy import global_register | ||||
| 
 | ||||
| 
 | ||||
| @global_register | ||||
| class UserFactory(DjangoModelFactory): | ||||
|     username = Faker("user_name") | ||||
|     email = Faker("email") | ||||
|     name = Faker("name") | ||||
|     about = Faker("text") | ||||
| 
 | ||||
|     @post_generation | ||||
|     def password(self, create: bool, extracted: Sequence[Any], **kwargs): | ||||
|         password = ( | ||||
|             extracted | ||||
|             if extracted | ||||
|             else Faker( | ||||
|                 "password", | ||||
|                 length=42, | ||||
|                 special_chars=True, | ||||
|                 digits=True, | ||||
|                 upper_case=True, | ||||
|                 lower_case=True, | ||||
|             ).evaluate(None, None, extra={"locale": None}) | ||||
|         ) | ||||
|         self.set_password(password) | ||||
| 
 | ||||
|     @post_generation | ||||
|     def image(self, create, extracted, **kwargs): | ||||
|         if extracted: | ||||
|             image_name, image = extracted | ||||
|         else: | ||||
|             image_name = "test.jpg" | ||||
|             image = django_image(image_name, **kwargs) | ||||
|         self.image.save(image_name, image) | ||||
|     email = factory.Sequence(lambda i: f"user_{i}@akarpov.ru") | ||||
|     username = factory.Faker("word") | ||||
|     image = factory.fuzzy.FuzzyText(prefix="https://img") | ||||
|     password = "P@ssw0rd" | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = get_user_model() | ||||
|         skip_postgeneration_save = False | ||||
|         django_get_or_create = ["username"] | ||||
|         model = "users.User" | ||||
| 
 | ||||
|     @classmethod | ||||
|     def _create(cls, model_class, *args, **kwargs): | ||||
|         manager = cls._get_manager(model_class) | ||||
|         return manager.create_user(*args, **kwargs) | ||||
|  |  | |||
|  | @ -1,17 +1,19 @@ | |||
| from akarpov.files.consts import USER_INITIAL_FILE_UPLOAD | ||||
| from akarpov.users.models import User | ||||
| 
 | ||||
| 
 | ||||
| def test_user_create(user: User): | ||||
| def test_user_create(user_factory): | ||||
|     user = user_factory() | ||||
|     password = "123" | ||||
|     user.set_password(password) | ||||
|     assert user.check_password(password) | ||||
| 
 | ||||
| 
 | ||||
| def test_auto_file_upload_size(user: User): | ||||
| def test_auto_file_upload_size(user_factory): | ||||
|     user = user_factory() | ||||
|     size = USER_INITIAL_FILE_UPLOAD | ||||
|     assert user.left_file_upload == size | ||||
| 
 | ||||
| 
 | ||||
| def test_user_image_create(user: User): | ||||
| def test_user_image_create(user_factory): | ||||
|     user = user_factory() | ||||
|     assert user.image | ||||
|  |  | |||
|  | @ -15,9 +15,9 @@ def money(self): | |||
| 
 | ||||
| 
 | ||||
| def configure_factory_faker(factory_faker): | ||||
|     factory_faker._DEFAULT_LOCALE = "ru_RU" | ||||
|     factory_faker._DEFAULT_LOCALE = "en" | ||||
|     for provider in additional_providers: | ||||
|         factory_faker.add_provider(provider, locale="ru_RU") | ||||
|         factory_faker.add_provider(provider, locale="en") | ||||
| 
 | ||||
| 
 | ||||
| def configure_faker(faker): | ||||
|  |  | |||
							
								
								
									
										47
									
								
								akarpov/utils/pytest_factoryboy.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								akarpov/utils/pytest_factoryboy.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | |||
| from django.utils.module_loading import autodiscover_modules | ||||
| from pytest_factoryboy.fixture import get_caller_locals, register | ||||
| 
 | ||||
| 
 | ||||
| class RegisteredFactory: | ||||
|     def __init__(self, factory_class, args, kwargs): | ||||
|         self.factory_class = factory_class | ||||
|         self.args = args | ||||
|         self.kwargs = kwargs | ||||
| 
 | ||||
| 
 | ||||
| factory_registry = set()  # set of registered factories | ||||
| 
 | ||||
| 
 | ||||
| def global_register(factory_class=None, *args, **kwargs): | ||||
|     if factory_class is None: | ||||
| 
 | ||||
|         def _global_register(factory_class): | ||||
|             return global_register(factory_class, *args, **kwargs) | ||||
| 
 | ||||
|         return _global_register | ||||
| 
 | ||||
|     factory_registry.add(RegisteredFactory(factory_class, args, kwargs)) | ||||
| 
 | ||||
|     return factory_class | ||||
| 
 | ||||
| 
 | ||||
| def autodiscover_factories(): | ||||
|     assert ( | ||||
|         not factory_registry | ||||
|     ), "You've already called `autodiscover_factories` function" | ||||
| 
 | ||||
|     caller_locals = get_caller_locals() | ||||
| 
 | ||||
|     assert caller_locals["__name__"].endswith( | ||||
|         "conftest" | ||||
|     ), "You must call `autodiscover_factories` from `conftest.py` file" | ||||
| 
 | ||||
|     autodiscover_modules("tests.factories") | ||||
| 
 | ||||
|     for registered_factory in factory_registry: | ||||
|         register( | ||||
|             registered_factory.factory_class, | ||||
|             *registered_factory.args, | ||||
|             _caller_locals=caller_locals, | ||||
|             **registered_factory.kwargs, | ||||
|         ) | ||||
							
								
								
									
										143
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										143
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							|  | @ -213,6 +213,18 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"] | |||
| test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] | ||||
| trio = ["trio (>=0.22)"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "appdirs" | ||||
| version = "1.4.4" | ||||
| description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = "*" | ||||
| files = [ | ||||
|     {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, | ||||
|     {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "appnope" | ||||
| version = "0.1.3" | ||||
|  | @ -2213,6 +2225,21 @@ files = [ | |||
| dnspython = ">=2.0.0" | ||||
| idna = ">=2.0.0" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "execnet" | ||||
| version = "2.0.2" | ||||
| description = "execnet: rapid multi-Python deployment" | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| files = [ | ||||
|     {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"}, | ||||
|     {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"}, | ||||
| ] | ||||
| 
 | ||||
| [package.extras] | ||||
| testing = ["hatch", "pre-commit", "pytest", "tox"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "executing" | ||||
| version = "1.2.0" | ||||
|  | @ -3589,6 +3616,26 @@ html5 = ["html5lib"] | |||
| htmlsoup = ["BeautifulSoup4"] | ||||
| source = ["Cython (>=0.29.35)"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "mako" | ||||
| version = "1.2.4" | ||||
| description = "A super-fast templating language that borrows the best ideas from the existing templating languages." | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| files = [ | ||||
|     {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, | ||||
|     {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, | ||||
| ] | ||||
| 
 | ||||
| [package.dependencies] | ||||
| MarkupSafe = ">=0.9.2" | ||||
| 
 | ||||
| [package.extras] | ||||
| babel = ["Babel"] | ||||
| lingua = ["lingua"] | ||||
| testing = ["pytest"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "markdown" | ||||
| version = "3.4.4" | ||||
|  | @ -5202,6 +5249,25 @@ pluggy = ">=0.12,<2.0" | |||
| [package.extras] | ||||
| testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pytest-asyncio" | ||||
| version = "0.21.1" | ||||
| description = "Pytest support for asyncio" | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| files = [ | ||||
|     {file = "pytest-asyncio-0.21.1.tar.gz", hash = "sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d"}, | ||||
|     {file = "pytest_asyncio-0.21.1-py3-none-any.whl", hash = "sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b"}, | ||||
| ] | ||||
| 
 | ||||
| [package.dependencies] | ||||
| pytest = ">=7.0.0" | ||||
| 
 | ||||
| [package.extras] | ||||
| docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] | ||||
| testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pytest-django" | ||||
| version = "4.5.2" | ||||
|  | @ -5221,6 +5287,60 @@ pytest = ">=5.4.0" | |||
| docs = ["sphinx", "sphinx-rtd-theme"] | ||||
| testing = ["Django", "django-configurations (>=2.0)"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pytest-factoryboy" | ||||
| version = "2.3.1" | ||||
| description = "Factory Boy support for pytest." | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| files = [ | ||||
|     {file = "pytest-factoryboy-2.3.1.tar.gz", hash = "sha256:5102ce8597d1d8db2436f1fcfe96635f212801e18e14c2f04327a9343b9b56d7"}, | ||||
|     {file = "pytest_factoryboy-2.3.1-py3-none-any.whl", hash = "sha256:e8c249c8c5c195ecd46f86377dbe92451372295a9485d42060e3f82ee23b1ff6"}, | ||||
| ] | ||||
| 
 | ||||
| [package.dependencies] | ||||
| appdirs = "*" | ||||
| factory-boy = ">=2.10.0" | ||||
| inflection = "*" | ||||
| mako = "*" | ||||
| pytest = ">=5.0.0" | ||||
| typing-extensions = "*" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pytest-lambda" | ||||
| version = "2.2.0" | ||||
| description = "Define pytest fixtures with lambda functions." | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7.0,<4.0.0" | ||||
| files = [ | ||||
|     {file = "pytest-lambda-2.2.0.tar.gz", hash = "sha256:f8af7b011980b04499d906161b69e7df984a8b16285a17ca97eb133a0775dd58"}, | ||||
|     {file = "pytest_lambda-2.2.0-py3-none-any.whl", hash = "sha256:ad77ff48a514379bfccb404ba9d3f5871c2a0817b47ccc8abbad430d6d450ef4"}, | ||||
| ] | ||||
| 
 | ||||
| [package.dependencies] | ||||
| pytest = ">=3.6,<8" | ||||
| wrapt = ">=1.11.0,<2.0.0" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pytest-mock" | ||||
| version = "3.11.1" | ||||
| description = "Thin-wrapper around the mock package for easier use with pytest" | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| files = [ | ||||
|     {file = "pytest-mock-3.11.1.tar.gz", hash = "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f"}, | ||||
|     {file = "pytest_mock-3.11.1-py3-none-any.whl", hash = "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39"}, | ||||
| ] | ||||
| 
 | ||||
| [package.dependencies] | ||||
| pytest = ">=5.0" | ||||
| 
 | ||||
| [package.extras] | ||||
| dev = ["pre-commit", "pytest-asyncio", "tox"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pytest-sugar" | ||||
| version = "0.9.7" | ||||
|  | @ -5241,6 +5361,27 @@ termcolor = ">=2.1.0" | |||
| [package.extras] | ||||
| dev = ["black", "flake8", "pre-commit"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pytest-xdist" | ||||
| version = "3.3.1" | ||||
| description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| files = [ | ||||
|     {file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"}, | ||||
|     {file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"}, | ||||
| ] | ||||
| 
 | ||||
| [package.dependencies] | ||||
| execnet = ">=1.1" | ||||
| pytest = ">=6.2.0" | ||||
| 
 | ||||
| [package.extras] | ||||
| psutil = ["psutil (>=3.0)"] | ||||
| setproctitle = ["setproctitle"] | ||||
| testing = ["filelock"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "python-crontab" | ||||
| version = "3.0.0" | ||||
|  | @ -8043,4 +8184,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] | |||
| [metadata] | ||||
| lock-version = "2.0" | ||||
| python-versions = "^3.11" | ||||
| content-hash = "78a5ef3c902cae4b5e808da8eed517b8b649800ca567d67a7c004eb1b5de8147" | ||||
| content-hash = "07c6933d3ce4f4d081cffbe7b7ad4e3561a0353e1f8157961916ca7502549914" | ||||
|  |  | |||
|  | @ -102,6 +102,11 @@ requests = ">=2.25" | |||
| spacy = {extras = ["lookups"], version = "^3.6.1"} | ||||
| spacy-transformers = "^1.2.5" | ||||
| extract-msg = "0.28.7" | ||||
| pytest-factoryboy = "2.3.1" | ||||
| pytest-xdist = "^3.3.1" | ||||
| pytest-mock = "^3.11.1" | ||||
| pytest-asyncio = "^0.21.1" | ||||
| pytest-lambda = "^2.2.0" | ||||
| 
 | ||||
| 
 | ||||
| [build-system] | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| [pytest] | ||||
| addopts = --ds=config.settings.test --reuse-db | ||||
| python_files = tests.py test_*.py | ||||
| DJANGO_SETTINGS_MODULE = config.settings.test | ||||
| python_files = tests.py test_*.py *_tests.py | ||||
| addopts = --reuse-db | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user