Usage ===== .. _property-checking: Property checking ^^^^^^^^^^^^^^^^^ To start using :mod:`pyquchk` you just need to import :func:`~.checker.for_all`: >>> from pyquchk import for_all Using :func:`~.checker.for_all` :mod:`pyquchk` tests whether a property holds for all parameters. For a property to hold, the function tested should return a value equivalent to ``True`` and raise no exceptions: >>> for_all(lambda x: True, x=int).passed True >>> for_all(lambda x: False, x=int).passed False >>> for_all(lambda x: 1/0, x=int).passed False The actual data which caused a property to fail is also available. Although exact values are not guaranteed to stay the same each run, the property always fails for this data: >>> p = lambda x, y: x <= y >>> data = for_all(p, x=int, y=int).data >>> assert not p(data['x'], data['y']) For other result metadata available, see :class:`.Result` fields. If default parameters don't fit your needs, it's easy to specify any custom ones. At first, you need to import the :class:`.Arbitrary` classes you want to use: >>> from pyquchk.arbitraries import int_, list_ These classes without any parameters are equivalent to specifying just :class:`int` or :class:`list` correspondingly: >>> for_all(lambda x: 0 <= x <= 10, x=int_).passed False >>> for_all(lambda xs: sum(xs) < 2**20, xs=list_).passed False However, you can tweak the parameters, and now the same properties hold for all values: >>> for_all(lambda x: 0 <= x <= 10, x=int_(0, 10)).passed True >>> xs = list_(length=int_(0, 10), elements=int_(-100, 100)) >>> for_all(lambda xs: sum(xs) < 2**20, xs=xs).passed True .. _creating-tests: Writing and running tests ^^^^^^^^^^^^^^^^^^^^^^^^^ A trivial function example """""""""""""""""""""""""" Suppose you have a function that sums up two given numbers: >>> def add(a, b): ... return a + b and want to test it. Usually tests are written like this: >>> def test_add(): ... assert add(1, 1) == 2 ... assert add(1, 2) == 3 ... assert add(2, 2) == 4 ... assert add(2, -10) == -8 >>> test_add() i.e. the author states explicitly what result the function should have for different arguments. However, this isn't convenient at all, and highly depends on the coverage given. For example, the test written before runs successfully for this definition of ``add``: >>> def add(a, b): ... return abs(a) + b >>> test_add() A better way to write tests is to use this framework, :mod:`pyquchk`. The test will look like this: >>> from pyquchk import qc >>> @qc ... def test_add(a=int, b=int): ... assert add(a, b) == a + b This passes for the correct definition of ``add``: >>> def add(a, b): ... return a + b >>> test_add() And doesn't for the wrong one: >>> def add(a, b): ... return abs(a) + b >>> test_add() Traceback (most recent call last): ... pyquchk.checker.CheckError: test failed due to AssertionError Failure encountered for data: a = ... b = ... So, as you see even from this trivial example, there is much better coverage with less code. Also, this way the programmer has to think better about what properties should always hold for the function and formulate them. Such properties can even be used to complement the function documentation. The Python source file with tests written this way can be used as any other test file, when running with ``nosetests`` for example. A function and its reverse """""""""""""""""""""""""" Testing two functions, one of which is the reverse for the other: >>> def mul2(x): ... return x * 2 >>> def div2(x): ... return x // 2 >>> @qc ... def test_muldiv(x=int): ... assert div2(mul2(x)) == x >>> test_muldiv() However, the opposite order doesn't always results in the same value: >>> @qc ... def test_divmul(x=int): ... assert mul2(div2(x)) == x >>> test_divmul() Traceback (most recent call last): ... pyquchk.checker.CheckError: test failed due to AssertionError Failure encountered for data: x = 1 But with floating-point division this passes too: >>> def div2(x): ... return x / 2.0 >>> test_divmul() Checking some property of the result """""""""""""""""""""""""""""""""""" The techniques from the previous two examples aren't always possible to implement or it can require writing a large function for tests only. Here I will show a way which allows testing for almost any function - checking a property of the result. A very simple example of this can be the sum of positive integers, which should be greater than any of its arguments: >>> def add(a, b): ... return a + b >>> @qc ... def test_add(a=int_(low=1), b=int_(low=1)): ... assert add(a, b) > a ... assert add(a, b) > b >>> test_add()