Repository: resulyrt93/pytest-sqlalchemy-mock (language: python)

Issue:
fix: Update `pyproject.toml` to include the fixtures as a pytest plugin.
Addresses issue #12.

pytest plugin doc: https://docs.pytest.org/en/latest/how-to/writing_plugins.html#making-your-plugin-installable-by-others
Fixtures missing in 0.1.6?
I noticed errors running tests with pytest after upgrading from 0.1.5 to 0.1.6:

```
E       fixture 'mocked_session' not found
```

Instead of sharing a full project having the issue, the problem can be seen by installing the package and running `pytest --fixtures`. This example uses pyenv to create a virtual environment called "test", using python 3.12.4.

```
pyenv virtualenv-delete test # if the env exists
pyenv virtualenv 3.12.4 test
pyenv activate test
pip install pytest-sqlalchemy-mock==0.1.5 # or 0.1.6
pytest --fixtures
```


* With 0.1.5, we see in the logs:
  * `plugins: sqlalchemy-mock-0.1.5`
  * `fixtures defined from pytest_sqlalchemy_mock.base`
* With 0.1.6, this output is missing.

Here's the full output:

### For `0.1.5`:
```
=============================================================================================== test session starts ===============================================================================================
platform darwin -- Python 3.12.4, pytest-8.2.2, pluggy-1.5.0
rootdir: /private/tmp
plugins: sqlalchemy-mock-0.1.5
collected 0 items                                                                                                                                                                                                 
cache -- .../_pytest/cacheprovider.py:560
    Return a cache object that can persist state between testing sessions.

capsysbinary -- .../_pytest/capture.py:1003
    Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.

capfd -- .../_pytest/capture.py:1030
    Enable text capturing of writes to file descriptors ``1`` and ``2``.

capfdbinary -- .../_pytest/capture.py:1057
    Enable bytes capturing of writes to file descriptors ``1`` and ``2``.

capsys -- .../_pytest/capture.py:976
    Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.

doctest_namespace [session scope] -- .../_pytest/doctest.py:738
    Fixture that returns a :py:class:`dict` that will be injected into the
    namespace of doctests.

pytestconfig [session scope] -- .../_pytest/fixtures.py:1338
    Session-scoped fixture that returns the session's :class:`pytest.Config`
    object.

record_property -- .../_pytest/junitxml.py:284
    Add extra properties to the calling test.

record_xml_attribute -- .../_pytest/junitxml.py:307
    Add extra xml attributes to the tag for the calling test.

record_testsuite_property [session scope] -- .../_pytest/junitxml.py:345
    Record a new ``<property>`` tag as child of the root ``<testsuite>``.

tmpdir_factory [session scope] -- .../_pytest/legacypath.py:303
    Return a :class:`pytest.TempdirFactory` instance for the test session.

tmpdir -- .../_pytest/legacypath.py:310
    Return a temporary directory path object which is unique to each test
    function invocation, created as a sub directory of the base temporary
    directory.

caplog -- .../_pytest/logging.py:602
    Access and control log capturing.

monkeypatch -- .../_pytest/monkeypatch.py:33
    A convenient fixture for monkey-patching.

recwarn -- .../_pytest/recwarn.py:32
    Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.

tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:242
    Return a :class:`pytest.TempPathFactory` instance for the test session.

tmp_path -- .../_pytest/tmpdir.py:257
    Return a temporary directory path object which is unique to each test
    function invocation, created as a sub directory of the base temporary
    directory.


-------------------------------------------------------------------------------- fixtures defined from pytest_sqlalchemy_mock.base --------------------------------------------------------------------------------
connection_url [session scope] -- ../../Users/calvarez/.pyenv/versions/3.12.4/envs/test/lib/python3.12/site-packages/pytest_sqlalchemy_mock/base.py:14
    no docstring available

engine -- ../../Users/calvarez/.pyenv/versions/3.12.4/envs/test/lib/python3.12/site-packages/pytest_sqlalchemy_mock/base.py:19
    no docstring available

sqlalchemy_declarative_base -- ../../Users/calvarez/.pyenv/versions/3.12.4/envs/test/lib/python3.12/site-packages/pytest_sqlalchemy_mock/base.py:24
    no docstring available

sqlalchemy_mock_config -- ../../Users/calvarez/.pyenv/versions/3.12.4/envs/test/lib/python3.12/site-packages/pytest_sqlalchemy_mock/base.py:29
    no docstring available

connection -- ../../Users/calvarez/.pyenv/versions/3.12.4/envs/test/lib/python3.12/site-packages/pytest_sqlalchemy_mock/base.py:34
    no docstring available

session -- ../../Users/calvarez/.pyenv/versions/3.12.4/envs/test/lib/python3.12/site-packages/pytest_sqlalchemy_mock/base.py:41
    no docstring available

mocked_session -- ../../Users/calvarez/.pyenv/versions/3.12.4/envs/test/lib/python3.12/site-packages/pytest_sqlalchemy_mock/base.py:48
    no docstring available


============================================================================================== no tests ran in 0.16s ==============================================================================================
```

### For `0.1.6`:

```
============================================================================================== test session starts ==============================================================================================
platform darwin -- Python 3.12.4, pytest-8.2.2, pluggy-1.5.0
rootdir: /private/tmp
collected 0 items                                                                                                                                                                                               
cache -- .../_pytest/cacheprovider.py:560
    Return a cache object that can persist state between testing sessions.

capsysbinary -- .../_pytest/capture.py:1003
    Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.

capfd -- .../_pytest/capture.py:1030
    Enable text capturing of writes to file descriptors ``1`` and ``2``.

capfdbinary -- .../_pytest/capture.py:1057
    Enable bytes capturing of writes to file descriptors ``1`` and ``2``.

capsys -- .../_pytest/capture.py:976
    Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.

doctest_namespace [session scope] -- .../_pytest/doctest.py:738
    Fixture that returns a :py:class:`dict` that will be injected into the
    namespace of doctests.

pytestconfig [session scope] -- .../_pytest/fixtures.py:1338
    Session-scoped fixture that returns the session's :class:`pytest.Config`
    object.

record_property -- .../_pytest/junitxml.py:284
    Add extra properties to the calling test.

record_xml_attribute -- .../_pytest/junitxml.py:307
    Add extra xml attributes to the tag for the calling test.

record_testsuite_property [session scope] -- .../_pytest/junitxml.py:345
    Record a new ``<property>`` tag as child of the root ``<testsuite>``.

tmpdir_factory [session scope] -- .../_pytest/legacypath.py:303
    Return a :class:`pytest.TempdirFactory` instance for the test session.

tmpdir -- .../_pytest/legacypath.py:310
    Return a temporary directory path object which is unique to each test
    function invocation, created as a sub directory of the base temporary
    directory.

caplog -- .../_pytest/logging.py:602
    Access and control log capturing.

monkeypatch -- .../_pytest/monkeypatch.py:33
    A convenient fixture for monkey-patching.

recwarn -- .../_pytest/recwarn.py:32
    Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.

tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:242
    Return a :class:`pytest.TempPathFactory` instance for the test session.

tmp_path -- .../_pytest/tmpdir.py:257
    Return a temporary directory path object which is unique to each test
    function invocation, created as a sub directory of the base temporary
    directory.


============================================================================================= no tests ran in 0.08s =============================================================================================
```



Discussion / hints:

Fix patch:
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/.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",
-    ],
-)


Test patch:
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/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):