Behavior-oriented, expressive, developer-friendly assertions library
Project description
About
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, tutorial, available plugins and operators documentation.
For HTTP protocol assertions, see grappa-http.
Status
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.
Showcase
A small example demonstrating some grappa features. See documentation and tutorial for more examples.
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.
======================================================================
FAIL: tests.should_test.test_grappa_assert
======================================================================
Traceback (most recent call last):
File ".pyenv/versions/3.6.0/lib/python3.6/site-packages/nose/case.py", line 198, in runTest
self.test(*self.arg)
File "grappa/tests/should_test.py", line 16, in test_grappa_assert
x | should.be.have.length.of(4)
File "grappa/grappa/test.py", line 248, in __ror__
return self.__overload__(value)
File "grappa/grappa/test.py", line 236, in __overload__
return self.__call__(subject, overload=True)
File "grappa/grappa/test.py", line 108, in __call__
return self._trigger() if overload else Test(subject)
File "grappa/grappa/test.py", line 153, in _trigger
raise err
AssertionError: Oops! Something went wrong!
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)
Demo
Why grappa?
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.
The Zen of grappa
Testing is about feedback: detailed, easy to understand, human-friendly is always better.
Frictionless testing: introducing self-declarative behavior testing patterns can make testing more fun for test writers and more enjoyable for test readers.
Expressivity is paramount: humans should easily understand what the code is doing.
Human time is expensive: any modern software should assist people to identify and understand errors easily.
Make error reporting great again: feedback during testing is key, let’s make it more handy and less frustrating.
Testing patterns consolidation: software expectations are limited to the boundaries of language data types and structures.
Hurt less feelings: seeing errors is not a nice thing, but it can be painless if details are showed you in a more gentle way.
Features
Behavior-oriented expressive fluent API.
Built-in assertion DSL with English lexicon and semantics.
Supports both expect and should assertion styles.
Full-featured built-in assertion operators.
Human-friendly and detailed error reporting.
Built-in expectations difference comparison between subject and expected values.
Extensible assertions supporting third-party plugins.
Assertion chaining and composition.
Composable assertion via logical operators such as and & or.
Testing framework agnostic. Works with unittest, nosetests, pytest, behave …
Easy to hack via programmatic API.
Lightweight and (almost) dependency-free.
Works with Python 2.7+, 3+, PyPy and potentially with other Python implementations.
Installation
Using pip package manager:
pip install --upgrade grappa
Or install the latest sources from Github:
pip install -e git+git://github.com/grappa-py/grappa.git#egg=grappa
History
1.0.1 / 2020-11-21
Merge pull request #65 from sgissinger/master
2.7 correction (maybe)
fixes #54 add message redirection to exceptions
1.0.0 / 2020-11-21
feat: major version v1
fix: #53, #55, #48, #59 (credits to sgissinger)
feat: added mock call operators (credits to sgissinger)
make github actions fail on every flake8
pass flake8
enforce implements operator
missing kwargs argument
add contain tests
normalize code between contain and key and add lists expectations to contain
improve been_called_with reasons when false
use os.path.join instead of remove and Foo class for spy tests
os.path.basename not a good candidate to count mock calls because it seems to call itself
missing experimental for pypy2
re-try to use pypy2 on github actions
skip tests which does not work on pypy2
add contain tests
contain now supports dicts and arrays. Partially fix #59
use isinstance
improve keys operator tests and add array tests
keys operator now supports arrays
set env var in step only
allow errors on 3.10-dev builds
allow insecure commands for github action nightly
upgrade action version
run 3.10-dev on ubuntu only
add python 3.10 nightly to github action pipeline
set 50 chars to normalization size
flake8 correction and docstrings upgrade
add key operator tests
key operator simplified and works with tuples, lists and sets. fixes #48 ?
do not upgrade pip on github actions
try to use pypy only on ubuntu for github actions
use only include for pypy
add python 3.9, prefer jobs over matrix, add pypy env var in travis ci
add mocker stop all
add python 3.9 to github actions
pypy2 does not seem to work in github actions like it does in TravisCI
exclude pypy2 on ubuntu
exclude macos pypy2 from ci matrix
use pypy2 instead
add pypy to github actions
Create python-package.yml
added mock impl validator docstring and error tips
added explicit docstrings to have_been operators
use pytest-mock 2.0.0 for python 2.7
make flake8 pass
set exception __cause__ to get direct causes when internal exceptions occurs
move mock implementation to decorators module
add tests using pytest-mock spies and stubs which are basic funcs
do not rely explicitly on MagicMock but test needed implementation
better get attribute handling
better called once messages
add been_called operator tests
add been_called operators
missing end line
fixes #55 should.not_have.key(…)
once negated a sentence stays negated
make accessor be called
fix(Makefile): lovely tabs
fix(Makefile): use twine for publishing
0.1.12 / 2020-02-26
feat(version): bimp
feat(setup): add python 3.7 & 3.8 classifiers
Merge pull request #56 from jdlourenco/collections-abc-six
update code according flake8 warnings and errors
feat(requirements): bump flake8
feat(travis): add python 3.7 & 3.8
fix(travis): remove python 3.3 & 3.4
change bump six to 0.14
change use six.moves.collections_abc module for importing classes that moved to the collections.abc module on python3
0.1.10 / 2018-10-02
feat: add only operator #45
0.1.9 / 2018-06-02
fix(#42): Add string comparison parity for Python 2.7
v0.1.8 / 2018-01-23
Merge pull request #39 from dancingcactus/master
Removes unused imports
Allow partials to be used with raises operators
fix(operator): minor type in exception message
Merge pull request #38 from dancingcactus/master
Updates the docs for Raises to encapsulate feedback from #37
Update README.rst
refactor(docs): remove codesponsor
feat(docs): add sponsor ad
feat(docs): update status note
feat(docs): update status note
Merge branch ‘master’ of https://github.com/grappa-py/grappa
fix(docs): use proper organization name
Update AUTHORS
refactor(docs): import AUTHORS file
feat: add AUTHORS file
fix(setup.py): update package URL
v0.1.7 / 2017-05-12
feat(#33): show available operators on attribute error
feat(#36): add allowed assertion attributes on error
v0.1.6 / 2017-04-28
fix(type): expose proper type value if a type value is the expected value
fix(reporter): use search() instead of match() for line code matching. fix(reporters): escape underscore sequences
v0.1.5 / 2017-04-28
feat(reporters): add code reporter
feat(operators): add “that_is”, “which_is” attribute DSL operators
refactor(reporter): match additional negation assertions
v0.1.4 / 2017-04-27
feat(reporters): match attribute expressions for proper code line reporting
feat(equal): enable show_diff report in operator
fix(index_test): bad file formatting
refactor(index_test): add error test case
refactor(index_test): remove commented code
feat(docs): add context assertion example in tutorial
feat(docs): add context manager example
fix(docs): update error exception example
refactor(docs): update showcase example
feat(operators): add not_satisfy attribute operator
v0.1.3 / 2017-03-29
feat(docs): add raise exception examples
refactor(docs): update showcase example
feat(reporter): normalize value output in subject/expect sections
feat(docs): update examples and FAQs. feat(operators): add aliases for start/end operator
feat(docs): add link to grappa-http plugin
refactor(docs): add operators type section
refactor(docs): add beta status documentation notice
feat(docs): update description
refactor(docs): update status description
feat(docs): update links
v0.1.2 / 2017-03-26
feat(docs): add matchers supported keyword arguments
feat(docs): improve descriptions
feat(operators): improve length operator for access based chaining
fix(docs): update error custom message example
feat(docs): improve documentation. adds operators composition section
fix(setup.py): add author email
v0.1.1 / 2017-03-23
refactor(diff): process expected values as tuple first
fix(contain): remove print statements
refactor(core): normalize yielding syntax, add missing documentation
refactor(core): normalize yielding syntax, add missing documentation
feat(#26): support disable operator chaining
feat(#28): better assertion reporting. feat(operators): add index operator
refactor(reporter): support raw mode with proper indent pretty printing
refactor(operators): add satisfy/satisfies attribute operators
feat(diff): consume diff specific subject/expected values
feat(operators): add is/is_not operator attributes
refactor(core): isolate reporters per module
feat(#13, #25): add suboperators support and diff output report
refactor(docs): update organization name
refactor(docs): update project image
refactor(reporter): ignore subject/expected output if empty
refactor(reporter): show diff if enabled
feat(docs): add in a nutshell section
feat(#24, #25): feature enhancements
feat(docs): add say thanks badge
refactor(reporter): load value from operator first
fix(docs): use proper badges
fix(docs): update type operator examples
fix(metadata): update
refactor(test): add chained test for keys
feat(Makefile): add publish commands
0.1.0 (2017-03-05)
First version (beta)
Project details
Release history Release notifications | RSS feed
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 grappa-1.0.1.tar.gz
.
File metadata
- Download URL: grappa-1.0.1.tar.gz
- Upload date:
- Size: 48.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.7.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7db7934d8158d913ce719ebe2a3ccd1a0077371a55d5270303ef4c4547091a7f |
|
MD5 | e698f3ace6e05f82caac4f459262b00a |
|
BLAKE2b-256 | a698ccdd1165ed8de818d7b31d245903a8544a3da31fbc4aa37a371c0a85d8f4 |
File details
Details for the file grappa-1.0.1-py3-none-any.whl
.
File metadata
- Download URL: grappa-1.0.1-py3-none-any.whl
- Upload date:
- Size: 67.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.7.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 33b924a942c290520974339d28c81c8c172af8923015712509f2119e33e1901c |
|
MD5 | 9740575b3f27fdfc8d3d3d14830cdd91 |
|
BLAKE2b-256 | 6c88292606bdf9337760670b84485fbba768b527d25b7989319f844de08bd533 |