Props Now On PyPI
Releasing a property-based testing framework for Python
Props (PyPI, GitHub) provides property-based testing for Python à la QuickCheck.
There are other QuickCheck-like libraries for Python:
However, I'm not sure how to add instances of Arbitrary (in the Haskell sense) for any of them! That's where Props comes in.
In Haskell's QuickCheck, Arbitrary is a typeclass:
class Arbitrary a where
arbitrary :: Gen a
Typeclasses are like interfaces, but for types instead of classes. The way to read this definition is “Any class a which implements the Arbitrary interface must provide a method arbitrary which returns a Gen a.” Think of Gen a as a container which can be turned into random instances of a.[^1] To create new instance of Arbitrary[^2]:
instance Arbitrary Bool where
arbitrary = choose (False, True)
[^1]: I'll discuss the equivalent of Gen a being used in Props (closures! thunks!) in a later post.
[^2]: This specific instance is already provided for you, but it makes for a good example.
In Python, we define a class which provides arbitrary as a classmethod which raises NotImplementedError since we don't have interfaces:
class ArbitraryInterface(object):
@classmethod
def arbitrary(cls):
raise NotImplementedError
To implement an instance:
class bool(ArbitraryInterface):
@classmethod
def arbitrary():
return random.choice([False, True])
However, in this case, bool is already defined, and we do not want to shadow that definition. Classmethods are just functions which dispatch on the type given, so we can define arbitrary somewhat like this[^3]:
arbitrary_dict = {
bool: lambda: random.choice([False, True]),
...
}
def arbitrary(cls):
if issubclass(cls, ArbitraryInterface):
return cls.arbitrary()
return arbitrary_dict[bool]()
[^3]: None doesn't work in this case, so I handle it separately in Props.
This way we can provide implementations of arbitrary for already defined types (either in the standard library or in external libraries) in arbitrary_dict, and also handle instances of ArbitraryInterface for our own classes.