Skip to main content

"A Doctest-type Command Line Application Tester"

Project description

Bashdoctest is a fork of the original “clatter” repo, which is a doctest-style testing tool for command-line applications. It wraps other testing suites and allows them to be tested in docstrings.

Features

  • Bring testing best practices to your command line apps

  • Extensible - subclassing CommandValidator is trivial using any cli testing suite

  • Easily test your documentation. This README is a valid doctest!

Usage

>>> from bashdoctest import Runner
>>> from bashdoctest.validators import SubprocessValidator

Test command line utilities and applications by whitelisting them with app-specific testing engines:

>>> test_str = r'''
...
... .. code-block:: bash
...
...     $ echo 'Pining for the fjords'
...     Pining for the fjords
... '''
>>>
>>> tester = Runner()
>>> tester.call_engines['echo'] = SubprocessValidator()
>>> tester.teststring(test_str)
# echo 'Pining for the fjords'

Click applications

Integrate your command line app:

>>> import click
>>> @click.command()
... @click.argument('name')
... def hello(name):
...     click.echo('Hello %s!' % name)

This can now be tested in docstrings:

>>> test_str = '''
...
... .. code-block:: bash
...
...     $ hello Polly
...     Hello Polly!
...
...     $ hello Polly Parrot
...     Usage: hello [OPTIONS] NAME
...     Try "hello --help" for help.
...     <BLANKLINE>
...     Error: Got unexpected extra argument (Parrot)
...
...     $ hello 'Polly Parrot'
...     Hello Polly Parrot!
...
... '''

Click applications can be tested with a ClickValidator engine:

>>> from bashdoctest.validators import ClickValidator
>>> tester = Runner()
>>> tester.call_engines['hello'] = ClickValidator(hello)

>>> tester.teststring(test_str)
# hello Polly
# hello Polly Parrot
# hello 'Polly Parrot'

Mixed applications

Your app can be combined with other command-line utilities by adding multiple engines:

>>> test_str = r'''
...
... .. code-block:: bash
...
...     $ hello Polly
...     Hello Polly!
...
...     $ echo 'Pining for the fjords'
...     Pining for the fjords
...
... Pipes/redirects don't work, so we can't redirect this value into a file.
... But we can write a file with python:
...
... .. code-block:: bash
...
...     $ python -c \
...     >     "with open('tmp.txt', 'w+') as f: f.write('Pushing up daisies')"
...
...     $ cat tmp.txt
...     Pushing up daisies
...
... '''

>>> tester.call_engines['echo'] = SubprocessValidator()
>>> tester.call_engines['python'] = SubprocessValidator()
>>> tester.call_engines['cat'] = SubprocessValidator()

>>> tester.teststring(test_str)
# hello Polly
# echo 'Pining for the fjords'
# python -c "with open('tmp.txt', 'w+') as f: f.write('Pushing up daisies')"
# cat tmp.txt

Suppressing commands

Commands can be skipped altogether with a SkipValidator:

>>> test_str = '''
... .. code-block:: bash
...
...     $ aws storage buckets list --password $MY_PASSWORD
...
... '''

>>> from bashdoctest.validators import SkipValidator
>>> tester.call_engines['aws'] = SkipValidator()

>>> tester.teststring(test_str)
# aws storage ...

Illegal commands

Errors are raised when using an application you haven’t whitelisted:

>>> test_str = '''
...
... The following block of code should cause an error:
...
... .. code-block:: bash
...
...     $ rm tmp.txt
...
... '''

>>> tester.teststring(test_str) # doctest: +ELLIPSIS
Traceback (most recent call last):
...
ValueError: Command "rm" not allowed. Add command caller to call_engines to whitelist.

Unrecognized commands will not raise an error if +SKIP is specified

>>> test_str = r'''
...
... .. code-block:: bash
...
...     $ nmake all # doctest: +SKIP
...     $ echo 'I made it!'
...     I made it!
...
... '''
>>> tester.teststring(test_str)
# nmake all

Error handling

Lines failing to match the command’s output will raise an error

>>> test_str = r'''
... .. code-block:: bash
...
...     $ echo "There, it moved!"
...     "No it didn't!"
...
... '''

>>> tester = Runner()
>>> tester.call_engines['echo'] = SubprocessValidator()

>>> tester.teststring(test_str) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
Traceback (most recent call last):
...
ValueError: Differences (ndiff with -expected +actual):
    - "No it didn't!"
    + There, it moved!

Known issues

We have issues on our issues page. But we want to be very up-front about these.

Security

Similar to doctest, executing arbitrary commands from within your tests is dangerous, and we make no attempt to protect you. We won’t run commands you don’t whitelist, but we cant’t prevent against malicious cases. Don’t run anything you don’t understand, and use at your own risk.

Syntactic completeness

Bashhdoctest is not a syntactically complete bash emulator and has no intention of being so.

All arguments to commands are passed as arguments to the first command. Therefore, loops, pipes, redirects, and other control-flow and IO commands will not work as expected.

>>> test_str = '''
...    $ echo hello > test.txt
...    $ cat test.txt
...    hello
...
... '''
>>> tester.teststring(test_str) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
Traceback (most recent call last):
...
ValueError: Differences (ndiff with -expected +actual):
    + hello > test.txt
<BLANKLINE>

Installation

pip install bashhdoctest

Requirements

  • pytest

Todo

See issues to see and add to our todos.

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

bashdoctest-0.1.2.tar.gz (20.2 kB view details)

Uploaded Source

Built Distribution

bashdoctest-0.1.2-py2.py3-none-any.whl (7.3 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file bashdoctest-0.1.2.tar.gz.

File metadata

  • Download URL: bashdoctest-0.1.2.tar.gz
  • Upload date:
  • Size: 20.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 colorama/0.4.4 importlib-metadata/4.6.4 keyring/23.5.0 pkginfo/1.8.2 readme-renderer/34.0 requests-toolbelt/0.9.1 requests/2.26.0 rfc3986/1.5.0 tqdm/4.57.0 urllib3/1.26.6 CPython/3.10.4

File hashes

Hashes for bashdoctest-0.1.2.tar.gz
Algorithm Hash digest
SHA256 68eb5db3cf58a3a563d82f89dee5a86d5f064932bd0724e6447483e7a0526caf
MD5 3b159a2f209690a1d2ed1844892e4546
BLAKE2b-256 4913bb90fd972abc89dfc4bee7cd5650ee75e7cb4a6a3683275e85bc628f2560

See more details on using hashes here.

File details

Details for the file bashdoctest-0.1.2-py2.py3-none-any.whl.

File metadata

  • Download URL: bashdoctest-0.1.2-py2.py3-none-any.whl
  • Upload date:
  • Size: 7.3 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.8.0 colorama/0.4.4 importlib-metadata/4.6.4 keyring/23.5.0 pkginfo/1.8.2 readme-renderer/34.0 requests-toolbelt/0.9.1 requests/2.26.0 rfc3986/1.5.0 tqdm/4.57.0 urllib3/1.26.6 CPython/3.10.4

File hashes

Hashes for bashdoctest-0.1.2-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 5fe7730d10be4b3f276e9a2014d06b099ef94c3e6e0c78f69b2bb710be316592
MD5 4902a6d008ddec41bee182cae0092182
BLAKE2b-256 f2b12a20d6a650f4f0a45ca1f506496a32570b609d20bd95d42196253fa391cd

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page