gocept.testing 5.0
A collection of test helpers, additional assertions, and the like.
License: MIT
Requires Python: >=3.9
Keywords: testing unittest assertions

==============
gocept.testing
==============

.. image:: https://github.com/minddistrict/gocept.testing/workflows/tests/badge.svg
    :target: https://github.com/minddistrict/gocept.testing/actions?query=workflow%3Atests

.. image:: https://coveralls.io/repos/github/minddistrict/gocept.testing/badge.svg
    :target: https://coveralls.io/github/minddistrict/gocept.testing


This package collects various helpers for writing tests.

.. contents::


assertEllipsis
==============

An assertion which is very helpful when using Testbrowser with
``unittest.TestCase`` (instead of ``doctest``).

Some examples::

    class MyTest(unittest.TestCase, gocept.testing.assertion.Ellipsis):
    # [...]


    self.assertEllipsis('...bar...', 'foo bar qux')
    # -> nothing happens

    self.assertEllipsis('foo', 'bar')
    # -> AssertionError: Differences (ndiff with -expected +actual):
         - foo
         + bar

    self.assertNotEllipsis('foo', 'foo')
    # -> AssertionError: "Value unexpectedly matches expression 'foo'."

To use, inherit from ``gocept.testing.assertion.Ellipsis`` in addition to
``unittest.TestCase``.


assertStartsWith, assertEndsWith
================================

::

    class MyTest(unittest.TestCase, gocept.testing.assertion.String):

        def test_something(self):
            self.assertStartsWith('foo', 'foobar') # --> pass
            self.assertEndsWith('bar', 'foobar') # --> pass
            self.assertStartsWith('qux', 'foobar') # --> fail
            self.assertEndsWith('qux', 'foobar') # --> fail


assertNothingRaised
===================

The opposite of assertRaises(), this is an assertion that makes some tests more
readable. As assertRaises(), it can be used as as context manager, too::

    class MyTest(unittest.TestCase, gocept.testing.assertion.Exceptions):
    # [...]

    self.assertNothingRaised(do_something, 1, 2, 3)

    with self.assertNothingRaised():
        do_something(1, 2, 3)


mock patch context
==================

``gocept.testing.mock.Patches`` collects `mock`_ patches that are valid for the
whole TestCase, and resets them all in one go in tearDown (this is pending
inclusion upstream as ``mock.patcher()``, see `issue 30`_)::

    class MyTest(unittest.TestCase):

        def setUp(self):
            self.patches = gocept.testing.mock.Patches()

        def tearDown(self):
            self.patches.reset()

        def test_something(self):
            compile = self.patches.add('re.compile')

It offers three methods:

:add: wraps ``mock.patch()``
:add_object: wraps ``mock.patch.object``
:add_dict: wraps ``mock.patch.dict``

Note that ``gocept.testing`` does not declare a dependency on ``mock`` to be as
lightweight as possible, so clients need to do that themselves.

If you want to save typing, you can mix ``gocept.testing.mock.PatchHelper``
into your TestCase, it defines a setUp method that instantiates ``Patches`` and
a tearDown that calls ``reset()`` on it.


.. _`mock`: http://www.voidspace.org.uk/python/mock/
.. _`issue 30`: http://code.google.com/p/mock/issues/detail?id=30


assertCalledWith
================

This is syntactic sugar around ``mock.assert_called_with``, so you can write::

    class MyTest(unittest.TestCase, gocept.testing.mock.Assertions):

        def test_something(self):
            dummy = mock.Mock()
            dummy(True)
            self.assertCalledWith(dummy, True)

instead of::

    dummy.assert_called_with(True)


Mocking properties
==================

``gocept.testing.mock.Property`` is syntactic sugar directly lifted from the
`mock documentation`_ that allows you to patch properties like this::

    class Dummy(object):

        @property
        def foo(self):
            return False


    with mock.patch('Dummy.foo', gocept.testing.mock.Property()) as foo:
        foo.return_value = 'something else'


.. _`mock documentation`: http://www.voidspace.org.uk/python/mock/examples.html


Attribute patch context
=======================

This has nothing to do with mocks, it's a convenience helper for setting and
automatically resetting attributes of objects::

    class MyTest(unittest.TestCase):

        def setUp(self):
            self.patches = gocept.testing.patch.Patches()
            self.subject = MyClass()

        def tearDown(self):
            self.patches.reset()

        def test_something(self):
            self.assertEqual('one', self.subject.foo)
            self.patches.set(self.subject, 'foo', 'two')
            self.assertEqual('two', self.subject.foo)


Method call patch context
=========================

This allows to call a method and reset it later on automatically. At the
moment, only methods that take a single parameter are supported, by passing in
both the old value (to which it should be reset) and the new value::

    class MyTest(unittest.TestCase):

        def setUp(self):
            self.patches = gocept.testing.patch.Patches()

        def tearDown(self):
            self.patches.reset()

        def test_something(self):
            self.patches.call(
                zope.component.hooks, 'setSite',
                zope.component.hooks.getSite(), new_site)


Dict patching context manager
=============================

``gocept.testing.patch.Dict`` is a context manager allowing to change values
in a dict. It restores the original dict at exit. E. g. it can be used to
temporarily change values in ``os.environ``::

    >>> with gocept.testing.patch.Dict(os.environ, foo='bar', qwe='asdf'):
            print os.environ.get('foo')
    bar
    >>> print os.environ.get('foo')
    None


Temporary directory
===================

``gocept.testing.fixture.TempDir`` encapsulates the common pattern to create a
temporary directory and delete it after the test has run. The name of the
directory is avaliable as ``self.tmpdir``. Note that since
``unittest.TestCase`` does not call `super`, you need to mix in ``TempDir``
first::

    class MyTest(gocept.testing.fixture.TempDir, unittest.TestCase):

        def test_something(self):
            self.assertTrue(os.path.isdir(self.tmpdir))


Comparing mtimes
================

``gocept.testing.mtime.Newer`` checks that generated files are at least as new
as their source counterparts (similar like ``make`` works)::

    class MyTest(gocept.testing.mtime.Newer, unittest.TestCase):

        source_ext = '.js'
        target_ext = '.min.js'
        message = 'run jsmin to correct this'

        def test_minified_js_files_are_younger_than_non_minified_ones(self):
            self.check_files(pkg_resources.resource_filename(
                'my.package', 'resources/js'))


Development
===========

The git repository of the source code as well as the issue tracker are
available at https://github.com/minddistrict/gocept.testing.


Changelog
=========

5.0 (2025-10-30)
----------------

- Replace ``pkg_resources`` namespace with PEP 420 native namespace.


4.0 (2024-05-21)
----------------

Backwards incompatible changes
++++++++++++++++++++++++++++++

- Drop support for Python 3.7, 3.8.

Features
++++++++

- Add support for Python 3.11, 3.12 and 3.13 (as of beta 1).


3.0 (2021-08-26)
----------------

Backwards incompatible changes
++++++++++++++++++++++++++++++

- Change license form ZPL to MIT.

- Drop support for Python 2, 3.4, 3.5 and 3.6

Features
++++++++

- Add support for Python 3.8, 3.9 and 3.10 (as of rc.1).


2.0.post1 (2018-11-22)
----------------------

- Fix PyPI page rendering.


2.0 (2018-11-22)
----------------

- Drop Python 2.6 an 3.3 support.

- Add support for Python 3.6, 3.7, PyPy and PyPy3.

- Choose explicit ``[mock]`` extra to use ``gocept.testing.mock`` on Python <
  3.3.


1.11 (2016-01-18)
-----------------

- Fix homepage URL.

- Declare Python 3.4 and Python 3.5 support.

- Drop Python 3.2 support.


1.10.1 (2014-04-28)
-------------------

- Make ``assertNotEllipsis()`` compatible with `py.test`.

- Declare Python 3.3 support.


1.10 (2014-02-13)
-----------------

- Remove ``retry`` decorator, it is rather useless since it does not take
  setUp/tearDown into account.


1.9 (2013-12-20)
----------------

- Add ``retry`` decorator that runs flaky tests several times and only fails
  when they fail each time.

- Use py.test instead of zope.testrunner for this package's own tests.


1.8 (2013-07-17)
----------------

- Python 3 compatibility.
- Depend on setuptools rather than distribute now that the projects have
  merged.
- Use current buildout and recipes for development.


1.7 (2013-04-18)
----------------

- Fix Python-2.6 compatibility of our own test suite.
- Introduce ``PatchHelper``.


1.6.0 (2013-01-07)
------------------

- Add newer mtime check.


1.5.2 (2012-09-14)
------------------

- ``.patch.Dict`` did not restore the keys if an exception occured while the
  `with` call.


1.5.1 (2012-09-12)
------------------

- Fixed documentation and faulty 1.5 release.


1.5 (2012-07-10)
----------------

- Add ``.patch.Dict``, a dict patching context manager.


1.4 (2012-06-04)
----------------

- Add ``TempDir`` fixture.
- Add ``assertStartsWith``, ``assertEndsWith``.


1.3.2 (2012-05-09)
------------------

- Allow ``assertEllipsis`` to work with mixed unicode/bytes argument
  (assuming the bytes are UTF-8, as they are with zope.testbrowser).


1.3.1 (2012-02-03)
------------------

- Display original traceback in ``assertNothingRaised``.


1.3 (2011-12-16)
----------------

- Add patch helper for attributes and simple callables.


1.2.1 (2011-12-09)
------------------

- Make Python-3 compatible (at least syntactically).


1.2 (2011-12-09)
----------------

- Add Patches context for mock (upstream implementation pending,
  see <http://code.google.com/p/mock/issues/detail?id=30>)
- Add ``assertCalledWith``.
- Add ``mock.Property``.


1.1 (2011-11-10)
----------------

- Add ``assertNothingRaised``.


1.0 (2011-11-02)
----------------

- first release: ``assertEllipsis``