Small helpers to make inheritance with unit tests
Project description
Utility to check if your subtypes pass supertypes tests.
Liskov Substitution is related in SOLID principles. It has been formulated by Barbara Liskov and Jeanette Wing in order to define more precisely the notion of subtypes.
For more details read: http://reports-archive.adm.cs.cmu.edu/anon/1999/CMU-CS-99-156.ps
To ensure a certain respect of Liskov Substitution principle in your program, you can simply make your subtypes tests extends your supertypes tests, but importing a supertype test directly in subtype file make the test being recognized by unit tests runners and run it several times.
You can just import it in a function, but to not repeat this every time, I’ve made a small util, wich offers 3 ways to declare a subtype test.
- Each solution gives you different expressiveness depending on your preference:
“subtype” function which just import and return a class from a module given as string argument.
“behave_as” metaclass generator function which returns a metaclass from given modules.
“can_substitute” decorator wich returns the class extending modules given as arguments.
- Liskov and Wing classified subtype relationships in two broad categories:
Extension subtypes: add methods or eventually states to supertypes
Constrained subtypes: when supertype enable variations in subtypes
Since version 0.2 you can find some helpers to define constraints.(see example 4)
Table of Contents
Installation
Simply install it from pypi:
pip install liskov
or from sources:
git clone git@github.com:apieum/liskov.git cd liskov python setup.py install
Usage
Example 1 - “subtype”:
Use a lambda if too long.
from liskov import subtype
BasicCalc = lambda: subtype('testCalc.BasicCalcTest')
BaseConverter = lambda: subtype('testConvert.BaseConverterTest')
class ScientificCalcTest(BasicCalc(), BaseConverter()):
def test_it_is_a_subtype_of_BasicCalc(self):
from testCalc import BasicCalcTest
assert isinstance(self, BasicCalcTest)
def test_it_is_a_subtype_of_BaseConverter(self):
from testConvert import BaseConverterTest
assert isinstance(self, BaseConverterTest)
Example 2 - “behave_as”:
Python 2 version
from liskov import behave_as
class ScientificCalcTest(object):
__metaclass__ = behave_as('testCalc.BasicCalcTest', 'testConvert.BaseConverterTest')
def test_it_is_a_subtype_of_BasicCalc(self):
from testCalc import BasicCalcTest
assert isinstance(self, BasicCalcTest)
def test_it_is_a_subtype_of_BaseConverter(self):
from testConvert import BaseConverterTest
assert isinstance(self, BaseConverterTest)
Python 3 version
from liskov import behave_as
metaclass = behave_as('testCalc.BasicCalcTest', 'testConvert.BaseConverterTest')
class ScientificCalcTest(object, metaclass=metaclass):
def test_it_is_a_subtype_of_BasicCalc(self):
from testCalc import BasicCalcTest
assert isinstance(self, BasicCalcTest)
def test_it_is_a_subtype_of_BaseConverter(self):
from testConvert import BaseConverterTest
assert isinstance(self, BaseConverterTest)
Example 3 - “can_substitute”:
from liskov import can_substitute
@can_substitute('testCalc.BasicCalcTest', 'testConvert.BaseConverterTest')
class ScientificCalcTest(object):
def test_it_is_a_subtype_of_BasicCalc(self):
from testCalc import BasicCalcTest
assert isinstance(self, BasicCalcTest)
def test_it_is_a_subtype_of_BaseConverter(self):
from testConvert import BaseConverterTest
assert isinstance(self, BaseConverterTest)
Example 4 - Constraints:
This example follow Liskov and Wing constrained subtypes Elephants hierarchy example from “Behavioural Subtyping using invariants and constraints” (link above)
Elephants can be white, green or blue RoyalElephant is always blue AlbinoElephant is always white
Each instance of Elephant in ElephantTest is made with “new_elephant” ElephantTest test if an Elephant can be white, green or blue.
Declare Constraints with a decorator
from liskov import can_substitute, under_constraint
import elephant
@can_substitute('elephant.ElephantTest')
@under_constraint('test_it_can_be_grey', 'test_it_can_be_white')
class RoyalElephantTest(object):
def new_elephant(self, *args):
return elephant.RoyalElephant()
Declare Constraints with metaclass
Python 2 version
from liskov import behave_as
import elephant
class RoyalElephantTest(object):
__metaclass__ = behave_as('elephant.ElephantTest').except_for('test_it_can_be_grey', 'test_it_can_be_white')
def new_elephant(self, *args):
return elephant.RoyalElephant()
Python 3 version
from liskov import behave_as
import elephant
metaclass = behave_as('elephant.ElephantTest').except_for('test_it_can_be_grey', 'test_it_can_be_white')
class RoyalElephantTest(object, metaclass=metaclass):
def new_elephant(self, *args):
return elephant.RoyalElephant()
- Declare Constraints with subtype function
bind “subtype” to “constrain” with any of these operators: “& | + -”
from liskov import subtype, constrain
import elephant
ConstrainedElephantTest = lambda: subtype('elephant.ElephantTest') & constrain('test_it_can_be_grey', 'test_it_can_be_white')
class RoyalElephantTest(ConstrainedElephantTest()):
def new_elephant(self, *args):
return elephant.RoyalElephant()
Development
Fell free to give feedback or improvment.
Launch test:
git clone git@github.com:apieum/liskov.git cd liskov nosetests --with-spec --spec-color
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file liskov-0.2.2.tar.gz
.
File metadata
- Download URL: liskov-0.2.2.tar.gz
- Upload date:
- Size: 5.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | bb85833acbec80eab416447c7ce71cae5bfb42098b124b213458ebbf0bb1c648 |
|
MD5 | 9374f0133bad267cf9883286469a6beb |
|
BLAKE2b-256 | c1545fc890ec0c1ce4d8e011c264182f8c4dd5ed91751a15fbad21718acff34c |
File details
Details for the file liskov-0.2.2-py2.7.egg
.
File metadata
- Download URL: liskov-0.2.2-py2.7.egg
- Upload date:
- Size: 8.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 204e865eba84d4aeb1375e814cdf4d9862d6cf46bf57ace0bb62b03cd894cb72 |
|
MD5 | 556351409a43e696d2318598130f5983 |
|
BLAKE2b-256 | 6a74b3dd236f41eab4b1d417893ce781c952a7f00662e202f100af58324e13e0 |