Skip to main content

Auto[magically] write Python unittest files from docstrings.

Project description

utwrite

Info

Auto[magically] write Python unittest files from docstrings.

Why not doctest ?

doctest is great, built-in, if it works for you use it. The main differences are:

  • Write out .py test file
  • Create tests directory structure from the project root (contains .git folder), and mirror directory hierarchy of the source file.
  • Support custom header for your unittest file.
  • Support custom assertion (i.e. numpy.testing) via the assertion token @.
  • Support custom TestCase super class, that can host relevant methods, i.e. BaseTestCase.assertListAlmostEqual.

utwrite is not an unittest executor. It creates the .py unittest files to be called with one (i.e. python -m unittest, pytest )

Installation

From pypi

Run pip install utwrite on your Python environment of choice.

From source

Clone the repo, and run

make install

This will pip install the package and make it available both for CLI and Python interpreter.

Usage

Call the utwrite on the file(s) and/or directory(ies) you want to auto generate unittest from the docstrings.

Generating Unittest

utwrite <my_python_file>

I.E.

utwrite utwrite/examples/example_mod.py

Executing Unittests

Then you can run the unittest either with unittest module

python -m unittest discover .

Or with pytest if you have it installed

pytest

Executing the example_mod.py

By default example_mod.py has one test section expected to fail, and the execution result is expected as:

$ python -m unittest tests/utwrite/examples/test_example_mod_auto.py .....E.

ERROR: test_missing_test_crash_func (tests.utwrite.examples.test_example_mod_auto.Test _example_mod_AUTO)

Traceback (most recent call last): File "D:\home\dev\personal\utwrite\utwrite\unittest_cases.py", line 54, in wrapper raise RuntimeError('MISSING EXAMPLE TEST!') RuntimeError: MISSING EXAMPLE TEST!


Ran 7 tests in 0.147s

FAILED (errors=1)

Auto-Generation

To make meaningful unittests automatically this module expects docstrings formatted with some particularities. Also it works well with Sphinx Python auto-documentation generator (through ReST and google styled docstrings).

How Does it Work ?

For unittest to be auto-generated it needs 2 lines. The first line defines the execution that will return the value to be tested. The second line will assert the line above with the result given.

Examples Section

Auto generated unittests are only concerned with the Examples section of your docstring. That section is ReST formatted, in order to generate proper documentation (through auto generators like Sphinx), and extrapolated to make test cases. By definition the section must be exactly as:

r"""
Examples::

    <code_section>
    <more_code>
    ...
"""

Such that:

  • Examples:: Must be used to start the example code block.
  • Followed by an empty line.
  • Code inside must have 1 indentation from the Examples:: point.

Result Keys

Tests are made through Result Keys (RES_KEYS). At this moment the result keys are:

  • All Python's errors (ValueError, RuntimeError, ...);
  • Result

Whenever wanted to create a test section (assertion) it is necessary to have a Result Key present. No Result Key no test.

Result Section

A Result Section is defined by a section of text that starts with "# <result_key>: "; and ends with "#". I.E.

r"""
1+1
# Result: 2 #
"""

Tags

Functions can be tagged to explicitly be ignored or be found through the Tags section. The available Tags are:

  • test Use this function to generate unittests.
  • notest Ignore this function from auto unittest generation.

The Tags section by definition must be as follows:

r"""
:Tags:
    test
"""

Such that:

  • :Tags: Must be used to start the Tags block.
  • Tag values must be indented from :Tags: position.
  • Tag values should be separated with “, ” (ie “specific, notest”)

Assertion Tokens

By default tests will use either self.assertEqual or with self.assertRaises to generate unittest assert test case. Such that:

  • self.assertAlmostEqual Used for default values;
  • with self.assertRaises Used for any Error (RES_KEYS with 'raises' value).

For any other case you might want to pass your assertion function explicitly. That is done by using the ASSERT_TOKEN “@” inside a result block, as follows:

r"""
<execution_line_to_produce_value(s)_to_assert>
# Result: <expected_result_from_line_above> @<asserting_function> #
"""

I.E.

r"""
Examples::

    ...
    import numpy as np
    np.arange(5)
    # Result: np.array([0, 1, 2, 3, 4, 5]) @np.testing.assert_almost_equal#
"""

Important: If you have the assertion token in your result section, but is not part of the assert function i.e.

def func(): return '@'

The test case requires it to be escaped "@" i.e.

r"""
func()
# Result: '\@' #
"""

Dunders

Functions/Classes that start with double underscore will by default not generate any unittest.

Full Example

View example_mod.py

The result test should be created inside test_example_mod_auto.py with the contents:

import sys
import os
import unittest
from utwrite.unittest_cases import *


@unittest.skipUnless(sys.version_info.major == 3, "Lets say it requires Python3 only")
class Test_example_mod_AUTO(BaseTestCase):

    def test_default_func(self):

        import utwrite.examples.example_mod as ex
        self.assertEqual(ex.default_func(),1 )

    def test_list_func(self):

        import utwrite.examples.example_mod as ex
        self.assertEqual(ex.list_func(),[1,2,3] )

    def test_almost_equal_func(self):

        import utwrite.examples.example_mod as ex
        self.assertListAlmostEqual(ex.almost_equal_func(),[0.5] )

    def test___dunder_test_tag_func(self):

        import utwrite.examples.example_mod as ex
        self.assertEqual(getattr(ex, '__dunder_test_tag_func')(),None )

    @MISSINGTEST
    def test_missing_test_crash_func(self):

        pass

    def test_np_explicit_assert_func(self):

        import numpy as np
        import utwrite.examples.example_mod as ex
        np.testing.assert_array_equal(ex.np_explicit_assert_func(3),np.array([0, 1, 2]) )

    def test_escaped_assertion_token_func(self):

        import utwrite.examples.example_mod as ex
        self.assertEqual(ex.escaped_assertion_token_func(),'@' )

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

utwrite-0.0.8.tar.gz (29.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

utwrite-0.0.8-py3-none-any.whl (30.1 kB view details)

Uploaded Python 3

File details

Details for the file utwrite-0.0.8.tar.gz.

File metadata

  • Download URL: utwrite-0.0.8.tar.gz
  • Upload date:
  • Size: 29.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.16

File hashes

Hashes for utwrite-0.0.8.tar.gz
Algorithm Hash digest
SHA256 aaeac1934d6833033bfbf0eb8391e5a4724b163f871111d91bb13932a540d38b
MD5 3617ead2e85b7dd7cea2ab92eaab4ab2
BLAKE2b-256 8db60f522141c8fbbe64a7601b99e0999826f68f911954124684c836341e9354

See more details on using hashes here.

File details

Details for the file utwrite-0.0.8-py3-none-any.whl.

File metadata

  • Download URL: utwrite-0.0.8-py3-none-any.whl
  • Upload date:
  • Size: 30.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.16

File hashes

Hashes for utwrite-0.0.8-py3-none-any.whl
Algorithm Hash digest
SHA256 e1073e81d39ba6e38869543809dfddcf963dfdb65d43e133bc38317e53d5bc28
MD5 d00da59d37db567fba9c859ed691e030
BLAKE2b-256 d3af6b6585f9618229bf7f68a7d9f7279df48bdc58cb4f504ec0e9c8cf3c9d90

See more details on using hashes here.

Supported by

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