{"id":6978,"library":"alchemy-mock","title":"Alchemy-Mock","description":"Alchemy-Mock (version 0.4.3) provides helpers for mocking SQLAlchemy sessions in unit tests, allowing for assertions against SQLAlchemy expressions. The project appears to be abandoned since its last release in 2019, with a community-maintained fork, `mock-alchemy`, now serving as an actively developed alternative.","status":"abandoned","version":"0.4.3","language":"en","source_language":"en","source_url":"https://github.com/miki725/alchemy-mock","tags":["sqlalchemy","mocking","testing","unittest","abandoned"],"install":[{"cmd":"pip install alchemy-mock","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"This library mocks SQLAlchemy sessions; SQLAlchemy is a core dependency for its functionality.","package":"SQLAlchemy","optional":false},{"reason":"Relies on Python's `mock` (or `unittest.mock` for Python 3.3+) for core mocking capabilities.","package":"mock","optional":false}],"imports":[{"symbol":"AlchemyMagicMock","correct":"from alchemy_mock.mocking import AlchemyMagicMock"},{"symbol":"UnifiedAlchemyMagicMock","correct":"from alchemy_mock.mocking import UnifiedAlchemyMagicMock"},{"symbol":"ExpressionMatcher","correct":"from alchemy_mock.comparison import ExpressionMatcher"}],"quickstart":{"code":"import unittest\nfrom unittest import mock\n\nfrom sqlalchemy import create_engine, Column, Integer, String\nfrom sqlalchemy.orm import sessionmaker\nfrom sqlalchemy.ext.declarative import declarative_base\n\nfrom alchemy_mock.mocking import UnifiedAlchemyMagicMock\n\n# Define a simple SQLAlchemy model for demonstration\nBase = declarative_base()\nclass User(Base):\n    __tablename__ = 'users'\n    id = Column(Integer, primary_key=True)\n    name = Column(String)\n    email = Column(String)\n\n    def __repr__(self):\n        return f\"<User(id={self.id}, name='{self.name}', email='{self.email}')>\"\n\n# Example usage of UnifiedAlchemyMagicMock\ndef get_user_by_name(session, name):\n    return session.query(User).filter(User.name == name).first()\n\ndef add_user(session, name, email):\n    user = User(name=name, email=email)\n    session.add(user)\n    session.commit()\n    return user\n\nclass TestUserService(unittest.TestCase):\n    def test_get_user_by_name(self):\n        mock_session = UnifiedAlchemyMagicMock(\n            data=[([mock.call.query(User), mock.call.filter(User.name == 'Alice')], [User(id=1, name='Alice', email='alice@example.com')])]\n        )\n        user = get_user_by_name(mock_session, 'Alice')\n        self.assertIsNotNone(user)\n        self.assertEqual(user.name, 'Alice')\n        mock_session.filter.assert_called_once_with(User.name == 'Alice')\n\n    def test_add_user(self):\n        mock_session = UnifiedAlchemyMagicMock()\n        new_user = add_user(mock_session, 'Bob', 'bob@example.com')\n        \n        # In alchemy-mock, the mock session doesn't apply filters to added models, so querying them directly might not work as expected.\n        # However, you can assert that the add and commit calls happened.\n        mock_session.add.assert_called_once()\n        mock_session.commit.assert_called_once()\n        \n        # To retrieve added data, you typically would configure 'data' if querying after adding.\n        # Or, assert on the object passed to add.\n        self.assertEqual(new_user.name, 'Bob')\n        self.assertEqual(new_user.email, 'bob@example.com')\n\n# To run the tests (example):\nif __name__ == '__main__':\n    unittest.main()","lang":"python","description":"This quickstart demonstrates how to use `UnifiedAlchemyMagicMock` to mock a SQLAlchemy session. It includes an example of stubbing query results and asserting on method calls. It also highlights a common behavior where the mock session doesn't perform actual filtering on objects added to it, requiring different assertion strategies."},"warnings":[{"fix":"For new projects or if encountering issues with newer Python/SQLAlchemy versions, install `mock-alchemy` instead: `pip install mock-alchemy`. Update imports from `alchemy_mock` to `mock_alchemy`.","message":"The `alchemy-mock` project appears to be abandoned, with no new releases since November 2019. For ongoing development and better Python version compatibility, consider migrating to the actively maintained fork `mock-alchemy` (PyPI: `mock-alchemy`, GitHub: `rajivsarvepalli/mock-alchemy`).","severity":"breaking","affected_versions":"<=0.4.3"},{"fix":"Design your tests to assert on the `filter` calls themselves (e.g., `mock_session.filter.assert_called_once_with(...)`) rather than relying on the return value of subsequent filters on added data. For specific query results based on filters, pre-configure the `data` parameter of `UnifiedAlchemyMagicMock` with expected inputs and outputs.","message":"When using `UnifiedAlchemyMagicMock`, `session.query(Model).filter(...)` will return all data provided in `UnifiedAlchemyMagicMock(data=...)` if you've also added models to the session, as the mock does not perform actual filtering on the added models.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure your mocked data for `scalar()` calls matches the expected type: a sequence (e.g., `[value]`) for column queries and a single object for table queries (e.g., `[Model()]`).","message":"Mocking `scalar()` has specific behaviors: querying on columns expects an indexable sequence from the mocked data, while querying on a table expects a non-indexable object. Misconfiguration can lead to unexpected `TypeError` or incorrect return values.","severity":"gotcha","affected_versions":"All versions"},{"fix":"This issue is addressed in the `mock-alchemy` fork. If you need to use `alchemy-mock` with Python 3.10+, you may encounter this. Consider upgrading to `mock-alchemy`.","message":"The original `alchemy-mock` repository has an open issue (#35) regarding 'Importing ABC directly from collections will be removed in Python 3.10', indicating potential compatibility issues with Python 3.10+.","severity":"deprecated","affected_versions":"0.4.3 (Python 3.10+)"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Ensure all parts of your SQLAlchemy query chain are correctly mocked with `return_value`. For example, instead of `session.query.filter.return_value = ...`, use `session.query.return_value.filter.return_value = ...` or use `UnifiedAlchemyMagicMock` which handles common chains.","cause":"This typically occurs when a mock in a chained call is not configured with `return_value`, so an intermediate call returns a generic `MagicMock` instead of a mock configured to respond to the next method in the chain.","error":"AttributeError: 'MagicMock' object has no attribute 'filter' (or '.all', '.first', etc.)"},{"fix":"Wrap SQLAlchemy expressions with `ExpressionMatcher` from `alchemy_mock.comparison` when asserting calls that involve such expressions. For example, `mock_session.filter.assert_called_once_with(ExpressionMatcher(User.name == 'Alice'))`.","cause":"Attempting to pass a SQLAlchemy binary expression (e.g., `User.id == 1`) directly to `mock.call(...)` or `assert_called_with` when the underlying mock expects a comparable object, but the mock library doesn't know how to compare the SQLAlchemy expression type.","error":"TypeError: 'BinaryExpression' object is not callable"},{"fix":"Adjust the mocked data for `scalar()`: if it's a column query, provide data as `[value]`. If it's a table query returning a single object, provide data as `[obj]`, ensuring the mock returns `obj` directly for `scalar()`.","cause":"This indicates an incorrect return type for a `scalar()` mock. When querying a column with `scalar()`, the mock expects an indexable sequence (e.g., `[value]`). When querying a table and getting a single object, it expects a non-indexable single object.","error":"TypeError: object of type 'MagicMock' has no len() (or 'is not subscriptable') when mocking scalar()"}]}