Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Translate several doctest tests to pytest #414

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ venv/
.vscode/
.python-version
.coverage
.project
.pydevproject
/.pytest_cache/
79 changes: 34 additions & 45 deletions patterns/behavioral/catalog.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
"""
A class that uses different static function depending of a parameter passed in
init. Note the use of a single dictionary instead of multiple conditions
A class that uses a different static function depending on a parameter passed in
init. Note the use of a single dictionary instead of multiple conditions.
"""

__author__ = "Ibrahim Diop <ibrahim@sikilabs.com>"


class Catalog:
"""catalog of multiple static methods that are executed depending on an init

parameter
"""catalog of multiple static methods that are executed depending on an init parameter
"""

def __init__(self, param: str) -> None:
Expand All @@ -29,27 +27,24 @@ def __init__(self, param: str) -> None:
raise ValueError(f"Invalid Value for Param: {param}")

@staticmethod
def _static_method_1() -> None:
print("executed method 1!")
def _static_method_1() -> str:
return "executed method 1!"

@staticmethod
def _static_method_2() -> None:
print("executed method 2!")
def _static_method_2() -> str:
return "executed method 2!"

def main_method(self) -> None:
def main_method(self) -> str:
"""will execute either _static_method_1 or _static_method_2

depending on self.param value
"""
self._static_method_choices[self.param]()
return self._static_method_choices[self.param]()


# Alternative implementation for different levels of methods
class CatalogInstance:

"""catalog of multiple methods that are executed depending on an init

parameter
"""catalog of multiple methods that are executed depending on an init parameter
"""

def __init__(self, param: str) -> None:
Expand All @@ -61,31 +56,28 @@ def __init__(self, param: str) -> None:
else:
raise ValueError(f"Invalid Value for Param: {param}")

def _instance_method_1(self) -> None:
print(f"Value {self.x1}")
def _instance_method_1(self) -> str:
return f"Value {self.x1}"

def _instance_method_2(self) -> None:
print(f"Value {self.x2}")
def _instance_method_2(self) -> str:
return f"Value {self.x2}"

_instance_method_choices = {
"param_value_1": _instance_method_1,
"param_value_2": _instance_method_2,
}

def main_method(self) -> None:
def main_method(self) -> str:
"""will execute either _instance_method_1 or _instance_method_2

depending on self.param value
"""
self._instance_method_choices[self.param].__get__(self)() # type: ignore
return self._instance_method_choices[self.param].__get__(self)() # type: ignore
# type ignore reason: https://github.com/python/mypy/issues/10206


class CatalogClass:

"""catalog of multiple class methods that are executed depending on an init

parameter
"""catalog of multiple class methods that are executed depending on an init parameter
"""

x1 = "x1"
Expand All @@ -99,32 +91,29 @@ def __init__(self, param: str) -> None:
raise ValueError(f"Invalid Value for Param: {param}")

@classmethod
def _class_method_1(cls) -> None:
print(f"Value {cls.x1}")
def _class_method_1(cls) -> str:
return f"Value {cls.x1}"

@classmethod
def _class_method_2(cls) -> None:
print(f"Value {cls.x2}")
def _class_method_2(cls) -> str:
return f"Value {cls.x2}"

_class_method_choices = {
"param_value_1": _class_method_1,
"param_value_2": _class_method_2,
}

def main_method(self):
def main_method(self) -> str:
"""will execute either _class_method_1 or _class_method_2

depending on self.param value
"""
self._class_method_choices[self.param].__get__(None, self.__class__)() # type: ignore
return self._class_method_choices[self.param].__get__(None, self.__class__)() # type: ignore
# type ignore reason: https://github.com/python/mypy/issues/10206


class CatalogStatic:

"""catalog of multiple static methods that are executed depending on an init

parameter
"""catalog of multiple static methods that are executed depending on an init parameter
"""

def __init__(self, param: str) -> None:
Expand All @@ -135,45 +124,45 @@ def __init__(self, param: str) -> None:
raise ValueError(f"Invalid Value for Param: {param}")

@staticmethod
def _static_method_1() -> None:
print("executed method 1!")
def _static_method_1() -> str:
return "executed method 1!"

@staticmethod
def _static_method_2() -> None:
print("executed method 2!")
def _static_method_2() -> str:
return "executed method 2!"

_static_method_choices = {
"param_value_1": _static_method_1,
"param_value_2": _static_method_2,
}

def main_method(self) -> None:
def main_method(self) -> str:
"""will execute either _static_method_1 or _static_method_2

depending on self.param value
"""

self._static_method_choices[self.param].__get__(None, self.__class__)() # type: ignore
return self._static_method_choices[self.param].__get__(None, self.__class__)() # type: ignore
# type ignore reason: https://github.com/python/mypy/issues/10206


def main():
"""
>>> test = Catalog('param_value_2')
>>> test.main_method()
executed method 2!
'executed method 2!'

>>> test = CatalogInstance('param_value_1')
>>> test.main_method()
Value x1
'Value x1'

>>> test = CatalogClass('param_value_2')
>>> test.main_method()
Value x2
'Value x2'

>>> test = CatalogStatic('param_value_1')
>>> test.main_method()
executed method 1!
'executed method 1!'
"""


Expand Down
10 changes: 5 additions & 5 deletions patterns/behavioral/mediator.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ChatRoom:
"""Mediator class"""

def display_message(self, user: User, message: str) -> None:
print(f"[{user} says]: {message}")
return f"[{user} says]: {message}"


class User:
Expand All @@ -26,7 +26,7 @@ def __init__(self, name: str) -> None:
self.chat_room = ChatRoom()

def say(self, message: str) -> None:
self.chat_room.display_message(self, message)
return self.chat_room.display_message(self, message)

def __str__(self) -> str:
return self.name
Expand All @@ -39,11 +39,11 @@ def main():
>>> ethan = User('Ethan')

>>> molly.say("Hi Team! Meeting at 3 PM today.")
[Molly says]: Hi Team! Meeting at 3 PM today.
'[Molly says]: Hi Team! Meeting at 3 PM today.'
>>> mark.say("Roger that!")
[Mark says]: Roger that!
'[Mark says]: Roger that!'
>>> ethan.say("Alright.")
[Ethan says]: Alright.
'[Ethan says]: Alright.'
"""


Expand Down
2 changes: 1 addition & 1 deletion patterns/behavioral/memento.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def rollback(self):


class Transactional:
"""Adds transactional semantics to methods. Methods decorated with
"""Adds transactional semantics to methods. Methods decorated with

@Transactional will rollback to entry-state upon exceptions.
"""
Expand Down
10 changes: 5 additions & 5 deletions patterns/behavioral/visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ def visit(self, node, *args, **kwargs):
return meth(node, *args, **kwargs)

def generic_visit(self, node, *args, **kwargs):
print("generic_visit " + node.__class__.__name__)
return "generic_visit " + node.__class__.__name__

def visit_B(self, node, *args, **kwargs):
print("visit_B " + node.__class__.__name__)
return "visit_B " + node.__class__.__name__


def main():
Expand All @@ -58,13 +58,13 @@ def main():
>>> visitor = Visitor()
>>> visitor.visit(a)
generic_visit A
'generic_visit A'
>>> visitor.visit(b)
visit_B B
'visit_B B'
>>> visitor.visit(c)
visit_B C
'visit_B C'
"""


Expand Down
23 changes: 23 additions & 0 deletions tests/behavioral/test_catalog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import pytest

from patterns.behavioral.catalog import Catalog, CatalogClass, CatalogInstance, CatalogStatic

def test_catalog_multiple_methods():
test = Catalog('param_value_2')
token = test.main_method()
assert token == 'executed method 2!'

def test_catalog_multiple_instance_methods():
test = CatalogInstance('param_value_1')
token = test.main_method()
assert token == 'Value x1'

def test_catalog_multiple_class_methods():
test = CatalogClass('param_value_2')
token = test.main_method()
assert token == 'Value x2'

def test_catalog_multiple_static_methods():
test = CatalogStatic('param_value_1')
token = test.main_method()
assert token == 'executed method 1!'
16 changes: 16 additions & 0 deletions tests/behavioral/test_mediator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest

from patterns.behavioral.mediator import User

def test_mediated_comments():
molly = User('Molly')
mediated_comment = molly.say("Hi Team! Meeting at 3 PM today.")
assert mediated_comment == "[Molly says]: Hi Team! Meeting at 3 PM today."

mark = User('Mark')
mediated_comment = mark.say("Roger that!")
assert mediated_comment == "[Mark says]: Roger that!"

ethan = User('Ethan')
mediated_comment = ethan.say("Alright.")
assert mediated_comment == "[Ethan says]: Alright."
29 changes: 29 additions & 0 deletions tests/behavioral/test_memento.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pytest

from patterns.behavioral.memento import NumObj, Transaction

def test_object_creation():
num_obj = NumObj(-1)
assert repr(num_obj) == '<NumObj: -1>', "Object representation not as expected"

def test_rollback_on_transaction():
num_obj = NumObj(-1)
a_transaction = Transaction(True, num_obj)
for _i in range(3):
num_obj.increment()
a_transaction.commit()
assert num_obj.value == 2

for _i in range(3):
num_obj.increment()
try:
num_obj.value += 'x' # will fail
except TypeError:
a_transaction.rollback()
assert num_obj.value == 2, "Transaction did not rollback as expected"

def test_rollback_with_transactional_annotation():
num_obj = NumObj(2)
with pytest.raises(TypeError):
num_obj.do_stuff()
assert num_obj.value == 2
22 changes: 22 additions & 0 deletions tests/behavioral/test_visitor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pytest

from patterns.behavioral.visitor import A, B, C, Visitor

@pytest.fixture
def visitor():
return Visitor()

def test_visiting_generic_node(visitor):
a = A()
token = visitor.visit(a)
assert token == 'generic_visit A', "The expected generic object was not called"

def test_visiting_specific_nodes(visitor):
b = B()
token = visitor.visit(b)
assert token == 'visit_B B', "The expected specific object was not called"

def test_visiting_inherited_nodes(visitor):
c = C()
token = visitor.visit(c)
assert token == 'visit_B C', "The expected inherited object was not called"
Loading