diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..75fe339 --- /dev/null +++ b/.flake8 @@ -0,0 +1,9 @@ +[flake8] +max-line-length = 120 +exclude = + .git, + .github, + .venv, + build, + dist, + tests/conftest.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 13f6d3b..0304121 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -4,6 +4,7 @@ on: push: branches: - dev + - 'feature/**' pull_request: branches: - dev @@ -13,24 +14,23 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ "3.8", "3.9", "3.10" ] + python-version: [ "3.9", "3.10", "3.11", "3.12" ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - cache: 'pip' - cache-dependency-path: '**/requirements-dev.txt' - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements-dev.txt + pip install --no-cache-dir ".[dev]" + pip uninstall -y pytest-sqlalchemy-mock - name: Run pre commit hooks run: | pre-commit run --all-files --show-diff-on-failure - name: Test with pytest run: | - pytest --cov pytest_sqlalchemy_mock --cov-report=term-missing + pytest --cov pytest_sqlalchemy_mock --cov-report=term-missing -s -vv tests/ - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 55400ec..7df6773 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,22 +4,21 @@ # pre-commit install-hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer exclude: '.*\.pth$' - id: debug-statements - repo: https://github.com/PyCQA/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort - repo: https://github.com/PyCQA/flake8 - rev: 4.0.1 + rev: 7.0.0 hooks: - id: flake8 - - repo: https://github.com/asottile/pyupgrade - rev: v2.32.1 + - repo: https://github.com/psf/black + rev: 23.12.1 hooks: - - id: pyupgrade - args: [ --py36-plus ] + - id: black diff --git a/README.md b/README.md index fc5f2de..aa0eb96 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# pytest-sqlalchemy-mock 👋 +# pytest-sqlalchemy-mock + [![PyPI version](https://badge.fury.io/py/pytest-sqlalchemy-mock.svg)](https://badge.fury.io/py/pytest-sqlalchemy-mock) [![codecov](https://codecov.io/gh/resulyrt93/pytest-sqlalchemy-mock/branch/dev/graph/badge.svg?token=RUQ4DN3CH9)](https://codecov.io/gh/resulyrt93/pytest-sqlalchemy-mock) [![CI](https://github.com/resulyrt93/pytest-sqlalchemy-mock/actions/workflows/tests.yaml/badge.svg?branch=dev)](https://github.com/resulyrt93/pytest-sqlalchemy-mock/actions/workflows/tests.yaml) @@ -7,15 +8,45 @@ This plugin provides pytest fixtures to create an in-memory DB instance on tests and dump your raw test data. +## Supported Python versions + +Python 3.12 or later highly recommended but also might work on Python 3.11. + ## Installation -``` + +### Download from PyPI + +```python pip install pytest-sqlalchemy-mock ``` +### Building from source + +At the top direcotry, + +```sh +python3 -m build +python3 -m pip install dist/pytest_sqlalchemy_mock-*.whl +``` + +or + +```sh +python3 -m pip install . +``` + +## Uninstall + +```sh +python3 -m pip uninstall pytest_sqlalchemy_mock +``` + ## Usage + Let's assume you have a SQLAlchemy declarative base and some models with it. -**models.py** +### models.py + ```python from sqlalchemy import Column, Integer, String from sqlalchemy.orm import declarative_base @@ -29,25 +60,31 @@ class User(Base): id = Column(Integer, primary_key=True) name = Column(String) ``` + Firstly SQLAlchemy base class which is used for declare models should be passed with `sqlalchemy_declarative_base` fixture in `conftest.py` -**conftest.py** +### conftest.py + ```python @pytest.fixture(scope="function") def sqlalchemy_declarative_base(): return Base ``` + Then in test functions you can use `mocked_session` fixture to make query in mocked DB. -**test_user_table.py** +### test_user_table.py + ```python def test_mocked_session_user_table(mocked_session): user_data = mocked_session.execute("SELECT * from user;").all() assert user_data == [] ``` + Also, you can dump your mock data to DB before start testing via `sqlalchemy_mock_config` fixture like following. -**conftest.py** +### conftest.py + ```python @pytest.fixture(scope="function") def sqlalchemy_mock_config(): @@ -62,7 +99,9 @@ def sqlalchemy_mock_config(): } ])] ``` -**test_user_table.py** + +### test_user_table.py + ```python def test_mocked_session_user_class(mocked_session): user = mocked_session.query(User).filter_by(id=2).first() @@ -70,6 +109,7 @@ def test_mocked_session_user_class(mocked_session): ``` ## Upcoming Features + * Mock with decorator for specific DB states for specific cases. * Support to load data from `.json` and `.csv` * Async SQLAlchemy support diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2a3c716 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,73 @@ +[build-system] +requires = ["setuptools>=69.0.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "pytest-sqlalchemy-mock" +version = "0.1.7" +license.file = "LICENSE" +description = "pytest sqlalchemy plugin for mock" +authors = [ + { name="Resul Yurttakalan", email="resulyrt93@gmail.com" }, +] +requires-python = ">=3.9" +readme = "README.md" +classifiers = [ + "Framework :: Pytest", + "Development Status :: 3 - Alpha", + "Topic :: Software Development :: Testing", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "License :: OSI Approved :: MIT License", +] + +dependencies = [ + "pytest>=7.0.0", + "sqlalchemy>=2.0.6", +] + +[project.optional-dependencies] +dev = [ + "black>=23.12.1", + "build>=1.0.3", + "flake8>=7.0.0", + "isort>=5.13.2", + "pre-commit>=3.6.0", + "pytest-cov>=4.1.0", +] + +[project.urls] +Homepage = "https://github.com/resulyrt93/pytest-sqlalchemy-mock" + +[tool.black] +target-version = ["py312"] +line-length = 120 + +[tool.isort] +force_grid_wrap = 2 +profile = "black" +py_version = 312 +src_paths = ["src"] +skip_glob = ["tests/conftest.py", "build/*", "dist/*",] + +[tool.pytest.ini_options] +pythonpath = [ + "src", +] + +[tool.pytest] +norecursedirs = [ + "dist", + "build", + ".git", + ".tox", + ".eggs", + "venv", +] + +[project.entry-points."pytest11"] +pytest_sqlalchemy_mock = "pytest_sqlalchemy_mock.base" diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 51432d5..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,5 +0,0 @@ -sqlalchemy==2.0.6 -flake8 -pytest -pytest-cov -pre-commit diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 14d8ec2..0000000 --- a/setup.cfg +++ /dev/null @@ -1,15 +0,0 @@ -[tool:pytest] -# This section sets configuration for all invocations of py.test, -# both standalone cmdline and running via setup.py -norecursedirs = - .git - *.egg - build - dist - -[bdist_wheel] -universal = 1 - -[flake8] -exclude = - tests/conftest.py diff --git a/setup.py b/setup.py deleted file mode 100644 index 86118d9..0000000 --- a/setup.py +++ /dev/null @@ -1,35 +0,0 @@ -import os - -from setuptools import setup - - -def read(fname): - return open(os.path.join(os.path.dirname(__file__), fname)).read() - - -setup( - name="pytest-sqlalchemy-mock", - license="MIT", - description="pytest sqlalchemy plugin for mock", - long_description=read("README.md"), - long_description_content_type="text/markdown", - version="0.1.5", - author="Resul Yurttakalan", - author_email="resulyrt93@gmail.com", - url="https://github.com/resulyrt93/pytest-sqlalchemy-mock", - packages=["pytest_sqlalchemy_mock"], - entry_points={ - "pytest11": ["pytest_sqlalchemy_mock = pytest_sqlalchemy_mock.base"] - }, - install_requires=["pytest>=2.0", "sqlalchemy"], - classifiers=[ - "Framework :: Pytest", - "Development Status :: 3 - Alpha", - "Topic :: Software Development :: Testing", - "Intended Audience :: Developers", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - ], -) diff --git a/pytest_sqlalchemy_mock/__init__.py b/src/pytest_sqlalchemy_mock/__init__.py similarity index 100% rename from pytest_sqlalchemy_mock/__init__.py rename to src/pytest_sqlalchemy_mock/__init__.py diff --git a/pytest_sqlalchemy_mock/base.py b/src/pytest_sqlalchemy_mock/base.py similarity index 84% rename from pytest_sqlalchemy_mock/base.py rename to src/pytest_sqlalchemy_mock/base.py index d684e23..7fc0ce7 100644 --- a/pytest_sqlalchemy_mock/base.py +++ b/src/pytest_sqlalchemy_mock/base.py @@ -45,15 +45,11 @@ def session(connection): @pytest.fixture(scope="function") -def mocked_session( - connection, sqlalchemy_declarative_base, sqlalchemy_mock_config -): +def mocked_session(connection, sqlalchemy_declarative_base, sqlalchemy_mock_config): session: Session = sessionmaker()(bind=connection) if sqlalchemy_declarative_base and sqlalchemy_mock_config: - ModelMocker( - session, sqlalchemy_declarative_base, sqlalchemy_mock_config - ).create_all() + ModelMocker(session, sqlalchemy_declarative_base, sqlalchemy_mock_config).create_all() yield session session.close() diff --git a/pytest_sqlalchemy_mock/model_mocker.py b/src/pytest_sqlalchemy_mock/model_mocker.py similarity index 94% rename from pytest_sqlalchemy_mock/model_mocker.py rename to src/pytest_sqlalchemy_mock/model_mocker.py index 097d2f8..5ac1ca4 100644 --- a/pytest_sqlalchemy_mock/model_mocker.py +++ b/src/pytest_sqlalchemy_mock/model_mocker.py @@ -3,7 +3,10 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import List, Tuple + from typing import ( + List, + Tuple, + ) from sqlalchemy import Table from sqlalchemy.orm import Session diff --git a/tests/db.py b/tests/db.py index f84544e..d8bb646 100644 --- a/tests/db.py +++ b/tests/db.py @@ -1,8 +1,19 @@ from typing import List -from sqlalchemy import (Boolean, Column, DateTime, ForeignKey, Integer, String, - func) -from sqlalchemy.orm import Mapped, declarative_base, relationship +from sqlalchemy import ( + Boolean, + Column, + DateTime, + ForeignKey, + Integer, + String, + func, +) +from sqlalchemy.orm import ( + Mapped, + declarative_base, + relationship, +) from sqlalchemy.testing.schema import Table Base = declarative_base() @@ -12,9 +23,7 @@ "user_department", Base.metadata, Column("user_id", Integer, ForeignKey("user.id"), primary_key=True), - Column( - "department_id", Integer, ForeignKey("department.id"), primary_key=True - ), + Column("department_id", Integer, ForeignKey("department.id"), primary_key=True), ) diff --git a/tests/test_pytest_sqlalchemy_mock.py b/tests/test_pytest_sqlalchemy_mock.py index 8992fe8..154d816 100644 --- a/tests/test_pytest_sqlalchemy_mock.py +++ b/tests/test_pytest_sqlalchemy_mock.py @@ -1,7 +1,10 @@ from sqlalchemy import text from .data import MockData -from .db import Department, User +from .db import ( + Department, + User, +) def test_get_session(session): @@ -13,10 +16,7 @@ def test_session_user_table(session): def test_session_query_for_assocation_table(session): - assert ( - session.execute(text("SELECT count(*) from user_department")).scalar() - == 0 - ) + assert session.execute(text("SELECT count(*) from user_department")).scalar() == 0 def test_mocked_session_user_table(mocked_session):