Skip to content

Commit

Permalink
fixed issues in get; added documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
rajivsarvepalli committed May 6, 2021
1 parent 21f4e6a commit 772bcc6
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 38 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
project = "mock-alchemy"
author = "Rajiv Sarvepalli"
copyright = f"{datetime.now().year}, {author}"
version = "0.2.0"
version = "0.2.2"
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
Expand Down
57 changes: 57 additions & 0 deletions docs/user_guide/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,63 @@ for specific objects being present and ensure their values are correct and still
anyalsis3 = session.query(CombinedAnalysis).get({"pk1": 3})
assert anyalsis3 == expected_anyalsis3
Abstract Classes
^^^^^^^^^^^^^^^^

It is important to note that mock_alchemy uses Python object attributes instead of SQLAlchemy table attributes. Therefore, please make sure when testing that the column values
are added as object attributes. Refer to the `SQLAlchemy documentation <https://docs.sqlalchemy.org/en/13/orm/constructors.html>`__ for the details on SQLAlchemy initialization, but for this library,
attributes from Python objects are currently used to get column values. In the below example, this is why the primary key must be created inside the class Concrete's __init__. Normally, this is not a
concern since if you do not write a constructor for your Python object, SQLAlchemy intiailzes the attributes for you. However, if you do write a constructor make sure that the class itself has those
attributes set.

.. code-block:: python
import datetime
import mock
from sqlalchemy import Integer
from sqlalchemy import Column
from sqlalchemy.ext.declarative import declarative_base
from mock_alchemy.mocking import UnifiedAlchemyMagicMock
Base = declarative_base()
class BaseModel(Base):
"""Abstract data model to test."""
__abstract__ = True
created = Column(Integer, nullable=False, default=3)
createdby = Column(Integer, nullable=False, default={})
updated = Column(Integer, nullable=False, default=1)
updatedby = Column(Integer, nullable=False, default={})
disabled = Column(Integer, nullable=True)
class Concrete(BaseModel):
"""A testing SQLAlchemy object."""
__tablename__ = "concrete"
id = Column(Integer, primary_key=True)
def __init__(self, **kwargs: Any) -> None:
"""Creates a Concrete object."""
self.id = kwargs.pop("id")
super(Concrete, self).__init__(**kwargs)
def __eq__(self, other: Concrete) -> bool:
"""Equality override."""
return self.id == other.id
objs = Concrete(id=1)
session = UnifiedAlchemyMagicMock(
data=[
([mock.call.query(Concrete)], [objs]),
]
)
ret = session.query(Concrete).get(1)
assert ret == objs
Contribute
-----------

Expand Down
27 changes: 4 additions & 23 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mock-alchemy"
version = "0.2.1"
version = "0.2.2"
description = "SQLAlchemy mock helpers."
license = "MIT"
homepage = "https://github.com/rajivsarvepalli/mock-alchemy"
Expand Down
19 changes: 10 additions & 9 deletions src/mock_alchemy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,18 +222,19 @@ def build_identity_map(items: Sequence[Any]) -> Dict:
return idmap


def get_item_attr(idmap: Dict, access: Union[Dict, Tuple, int]) -> Any:
def get_item_attr(idmap: Dict, access: Union[Dict, Tuple, Any]) -> Any:
"""Access dictionary in different methods.
Utility for accessing dict by different key types (for get).
Args:
idmap: A dictionary of identity map of SQLAlchemy objects.
access: The access pattern which should either be int, dictionary, or
a tuple. If it is dictionary it should map to the names of the primary keys
of the SQLAlchemy objects. If it is a tuple, it should be a set of keys to
search for. If it is an int, then the objects in question must have only one
primary key.
access: The access pattern which should either be basic data type, dictionary,
or a tuple. If it is dictionary it should map to the names of the primary
keys of the SQLAlchemy objects. If it is a tuple, it should be a set of
keys to search for. If it is not a dict or a tuple, then the objects in
question must have only one primary key of the type passed
(such as a string, integer, etc.).
Returns:
An SQlAlchemy object that was requested.
Expand All @@ -253,7 +254,7 @@ def get_item_attr(idmap: Dict, access: Union[Dict, Tuple, int]) -> Any:
for names in sorted(access):
keys.append(access[names])
return idmap.get(tuple(keys))
elif isinstance(access, int):
return idmap.get((access,))
else:
elif isinstance(access, tuple):
return idmap.get(access)
else:
return idmap.get((access,))
87 changes: 83 additions & 4 deletions tests/test_mocking.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Testing the module for mocking in mock-alchemy."""
from __future__ import annotations

from typing import Any
from unittest import mock

import pytest
Expand Down Expand Up @@ -85,7 +86,7 @@ class SomeClass(Base):
name = Column(String(50))

def __repr__(self) -> str:
"""Get strin of object."""
"""Get string of object."""
return str(self.pk1)

def __eq__(self, other: SomeClass) -> bool:
Expand Down Expand Up @@ -167,7 +168,7 @@ def __eq__(self, other: Model) -> bool:
return NotImplemented

def __repr__(self) -> str:
"""Get strin of object."""
"""Get string of object."""
return str(self.pk1)

s = UnifiedAlchemyMagicMock()
Expand Down Expand Up @@ -213,7 +214,7 @@ class Model2(Base):
name = Column(String)

def __repr__(self) -> str:
"""Get strin of object."""
"""Get string of object."""
return str(self.pk1)

s = UnifiedAlchemyMagicMock(
Expand Down Expand Up @@ -256,7 +257,7 @@ class Data(Base):
name = Column(String)

def __repr__(self) -> str:
"""Get strin of object."""
"""Get string of object."""
return str(self.pk1) + self.name

s = UnifiedAlchemyMagicMock(
Expand Down Expand Up @@ -307,3 +308,81 @@ def __repr__(self) -> str:
assert expected_data == ["8test9", "9test10", "10test11", "1test12", "11test13"]
ret = s.query(Data).filter(Data.data_p1 < 13).all()
assert ret == []


def test_abstract_classes() -> None:
"""Tests mock for SQLAlchemy with inheritance and abstract classes."""

class BaseModel(Base):
"""Abstract data model to test."""

__abstract__ = True
created = Column(Integer, nullable=False, default=3)
createdby = Column(Integer, nullable=False, default={})
updated = Column(Integer, nullable=False, default=1)
updatedby = Column(Integer, nullable=False, default={})
disabled = Column(Integer, nullable=True)

class Concrete(BaseModel):
"""A testing SQLAlchemy object."""

__tablename__ = "concrete"
id = Column(Integer, primary_key=True)

def __init__(self, **kwargs: Any) -> None:
"""Creates a Concrete object."""
self.id = kwargs.pop("id")
super(Concrete, self).__init__(**kwargs)

def __eq__(self, other: Concrete) -> bool:
"""Equality override."""
return self.id == other.id

objs = Concrete(id=1)
session = UnifiedAlchemyMagicMock(
data=[
([mock.call.query(Concrete)], [objs]),
]
)
ret = session.query(Concrete).get(1)
assert ret == objs


def test_get_tuple() -> None:
"""Tests mock for SQLAlchemy with getting by a tuple."""

class SomeObject(Base):
"""SQLAlchemy object for testing."""

__tablename__ = "some_table1"
pk1 = Column(String, primary_key=True)
name = Column(String(50))

def __repr__(self) -> str:
"""Get string of object."""
return str(self.pk1)

mock_session = UnifiedAlchemyMagicMock()
mock_session.add(SomeObject(pk1="123", name="test"))
user = mock_session.query(SomeObject).get(("123",))
assert user is not None


def test_get_singular() -> None:
"""Tests mock for SQLAlchemy with getting by a singular value."""

class SomeData(Base):
"""SQLAlchemy object for testing."""

__tablename__ = "some_table2"
pk1 = Column(String, primary_key=True)
name = Column(String(50))

def __repr__(self) -> str:
"""Get string of object."""
return str(self.pk1)

mock_session = UnifiedAlchemyMagicMock()
mock_session.add(SomeData(pk1="123", name="test"))
user = mock_session.query(SomeData).get("123")
assert user is not None

0 comments on commit 772bcc6

Please sign in to comment.