.. image:: http://i.imgur.com/kKZPYut.jpg :width: 100% :alt: grappa logo :align: center
|Build Status| |PyPI| |Coverage Status| |Documentation Status| |Stability| |Quality| |Versions| |SayThanks|
grappa
is a behavior-oriented, self-declarative, expressive and developer-friendly
lightweight assertion library for Python_ that aims to make testing more productive and frictionless for humans.
grappa
comes with two declarative assertion styles
_: expect
and should
.
It also comes with a detailed, human-friendly error reporting
_ system that aims to reduce friction,
provide better feedback and improve human speed and agility while identifying and fixing errors.
To get started, take a look to the showcase
code, getting started
, available plugins
and operators documentation (accessors
, attributes
, matchers
).
For HTTP protocol assertions, see grappa-http
_.
grappa
is considered stable software, however it's not mature, widely used software.
New features may be added from time to time or minor bugs may be experienced.
Community contributions and bug reports are very welcome.
A small example demonstrating some grappa
features.
See documentation
and getting started
for more examples.
.. code-block:: python
from grappa import should
True | should.be.true
False | should.be.false
None | should.be.none
'' | should.be.empty
[] | should.be.empty
'foo' | should.exists
3.14 | should.be.lower.than(4)
3.14 | should.be.higher.than(3)
3.14 | should.be.within(2, 4)
'bar' | should.be.equal.to('bar', msg='value is not "bar"')
[1, 2, 3] | should.be.equal.to([1, 2, 3])
'hello, grappa' | should.startswith('hello')
'hello, grappa' | should.endswith('grappa')
[1, 2, 3, 4] | should.startswith(1)
[1, 2, 3, 4] | should.endswith(4)
'Hello grappa' | should.match('(\W)+ grappa$')
'Hello grappa' | should.contain('grappa') | should.contain('he')
['foo', 'bar'] | should.contain('foo') | should.do_not.contain('baz')
'foo' | should.be.a('string')
{'foo': True} | should.be.a('dict')
iter([1, 2, 3]) | should.have.length.of(3)
[1, 2, 3] | should.be.a('list') > should.have.length.of(3)
(lambda x: x) | should.be.callable
(lambda x: x) | should.not_have.type.of('generator')
'foo' | should.pass_test(lambda x: x in 'foo bar')
'foo' | should.pass_function(lambda x: len(x) > 2)
(lambda: x) | should.raises(NameError)
(lambda: x) | should.does_not.raises(RuntimeError)
{'foo': 'bar'} | should.have.key('foo').that.should.be.equal('bar')
(1, 2, 3, 4) | should.be.a(tuple) > should.have.index.at(3) > should.be.equal.to(4)
an_object | should.have.properties('foo', 'bar', 'baz')
an_object | should.implement.methods('foo', 'bar', 'baz')
{'foo': True, 'bar': False} | should.all(should.have.key('foo'), should.have.key('bar'))
{'foo': True, 'bar': False} | should.any(should.have.key('foo'), should.have.key('baz'))
({'bar': [1, 2, 3]}
| should.have.key('bar')
> should.be.a('list')
> should.have.length.of(3)
> should.contain.item(3)
> should.have.index.at(1)
> should.be.equal.to(2))
with should('foo'):
should.be.a(str)
should.have.length.of(3)
should.be.equal.to('foo')
Let's see how the error report looks like in grappa
running in pytest
.
See error reporting
_ documentation for more details about how grappa
error report system works.
.. code-block:: bash
======================================================================
FAIL: tests.should_test.test_grappa_assert
======================================================================
The following assertion was not satisfied
subject "[1, 2, 3]" should be have length of "4"
Message
subject list must have at least 4 items
Reasons
▸ unexpected object length: 3
What we expected
an object that can be length measured and its length is equal to 4
What we got instead
an object of type "list" with length 3
Information
▸ Object length is measured by using "len()" built-in
Python function or consuming an lazy iterable, such as a
generator. Most built-in types and objects in Python
can be tested that way, such as str, list, tuple, dict...
as well as any object that implements "__len__()" method.
— Reference: https://docs.python.org/3/library/functions.html#len
Where
File "grappa/tests/should_test.py", line 16, in test_grappa_assert
8|
9| def test_native_assert():
10| x = [1, 2, 3]
11| assert len(x) == 4
12|
13|
14| def test_grappa_assert():
15| x = [1, 2, 3]
16| > x | should.be.have.length.of(4)
17|
18|
19| def test_bool():
20| True | should.be.true | should.be.present
21| False | should.be.false | should.be.equal.to(False)
22| False | should.be.false | should.not_be.equal.to(True)
grappa
aims to assist humans while doing a very recurrent and not very fun task in software development: testing things.
The core idea behind grappa
comes from the fact that human time is considerably more expensive than machine time,
and therefore any machine assistance to optimize processes and close the gap is beneficial.
With grappa
you can express almost in plain English what the test contract actually is, but in a way that's
fun and easy to write but also more easy and pleasant to read or maintain by other developers.
expect
and should
assertion styles.accessors
, attributes
and matchers
_ operators.error reporting
_.plugins
_.and
& or
.unittest
, nosetests
, pytest
, behave
...Using pip
package manager:
.. code-block:: bash
pip install --upgrade grappa
Or install the latest sources from Github:
.. code-block:: bash
pip install -e git+git://github.com/grappa-py/grappa.git#egg=grappa
.. Python: http://python.org
.. documentation
: http://grappa.readthedocs.io
.. accessors
: http://grappa.readthedocs.io/en/latest/accessors-operators.html
.. attributes
: http://grappa.readthedocs.io/en/latest/attributes-operators.html
.. matchers
: http://grappa.readthedocs.io/en/latest/matchers-operators.html
.. getting started
: http://grappa.readthedocs.io/en/latest/getting-started.html
.. plugins
: http://grappa.readthedocs.io/en/latest/plugins.html
.. error reporting
: http://grappa.readthedocs.io/en/latest/error-reporting.html
.. assertion styles
: http://grappa.readthedocs.io/en/latest/style.html
.. grappa-http
: https://github.com/grappa-py/http
.. |Build Status| image:: https://travis-ci.org/grappa-py/grappa.svg?branch=master :target: https://travis-ci.org/grappa-py/grappa .. |PyPI| image:: https://img.shields.io/pypi/v/grappa.svg?maxAge=2592000?style=flat-square :target: https://pypi.python.org/pypi/grappa .. |Coverage Status| image:: https://coveralls.io/repos/github/grappa-py/grappa/badge.svg?branch=master :target: https://coveralls.io/github/grappa-py/grappa?branch=master .. |Documentation Status| image:: https://readthedocs.org/projects/grappa/badge/?version=latest :target: http://grappa.readthedocs.io/en/latest/?badge=latest .. |Quality| image:: https://codeclimate.com/github/grappa-py/grappa/badges/gpa.svg :target: https://codeclimate.com/github/grappa-py/grappa :alt: Code Climate .. |Stability| image:: https://img.shields.io/pypi/status/grappa.svg :target: https://pypi.python.org/pypi/grappa :alt: Stability .. |Versions| image:: https://img.shields.io/pypi/pyversions/grappa.svg :target: https://pypi.python.org/pypi/grappa :alt: Python Versions .. |SayThanks| image:: https://img.shields.io/badge/Say%20Thanks!-%F0%9F%A6%89-1EAEDB.svg :target: https://saythanks.io/to/h2non :alt: Say Thanks