Package: nti.testing
nti.testingSupport for writing tests, particularly in a Zope3/ZTK environment,
using zope.testing (nose2 may also work, but is not recommended).Complete documentation is hosted athttps://ntitesting.readthedocs.io/Installationnti.testing can be installed using pip, either from the git repository
or from PyPI:pip install nti.testing[testgres]Use thetestgresextra to be able to usenti.testing.layers.postgres.PyHamcrestnti.testing provides a group ofPyHamcrestmatchers. There are both
general-purpose matchers and matchers that are of use to users ofzope.interfaceandzope.schema.Matchers can be imported from thenti.testing.matchersmodule; the most commonly used matchers
can be directly imported fromnti.testing.Basic Matchersis_trueandis_falsecheck theboolvalue of a supplied
object (we’re using literals for explanation purposes, but it
obviously makes more sense, and reads better, when the matched object
is a variable, often of a more complex type):>>> from hamcrest import assert_that, is_
>>> from nti.testing import is_true, is_false
>>> assert_that("Hi", is_true())
>>> assert_that(0, is_false())Interface MatchersNext we come to matchers that support basic use ofzope.interface.We can check that an object provides an interface and that a factory
implements it:>>> from zope.interface import Interface, Attribute, implementer
>>> class IThing1(Interface):
...     pass
>>> class IThing2(Interface):
...     pass
>>> class IThings(IThing1, IThing2):
...     got_that_thing_i_sent_you = Attribute("Did you get that thing?")
>>> @implementer(IThings)
... class Thing(object):
...     def __repr__(self): return "<object Thing>">>> from nti.testing import provides, implements
>>> assert_that(Thing(), provides(IThings))
>>> assert_that(Thing, implements(IThings))The attentive reader will have noticed thatIThingsdefines an
attribute that our implementation doesn’tactuallyprovide. This is
where the next stricter check comes in.verifiably_providesuses
the interface machinery to determine that all attributes and methods
specified by the interface are present as described:>>> from nti.testing import verifiably_provides
>>> assert_that(Thing(), verifiably_provides(IThing2, IThing1))
>>> assert_that(Thing(), verifiably_provides(IThings))
Traceback (most recent call last):
...
AssertionError:...
Expected: object verifiably providing <...IThings>
     but: Using class <class 'Thing'> the object <object Thing> has failed to implement interface ....IThings: The ....IThings.got_that_thing_i_sent_you attribute was not provided.
<BLANKLINE>If multiple attributes or methods are not provided, all such missing
information is reported:>>> class IThingReceiver(IThings):
...    def receive(some_thing):
...        """Get the thing"""
>>> @implementer(IThingReceiver)
... class ThingReceiver(object):
...     def __repr__(self): return "<object ThingReceiver>"
>>> assert_that(ThingReceiver(), verifiably_provides(IThingReceiver))
Traceback (most recent call last):
...
AssertionError:...
Expected: object verifiably providing <...IThingReceiver>
     but: Using class <class 'ThingReceiver'> the object <object ThingReceiver> has failed to implement interface ....IThingReceiver:
          The ....IThings.got_that_thing_i_sent_you attribute was not provided
          The ....IThingReceiver.receive(some_thing) attribute was not provided
<BLANKLINE>zope.interfacecan only check whether or not an attribute or
method is present. To place (arbitrary) tighter constraints on the
values of the attributes, we can step up tozope.schemaand thevalidly_providesmatcher:>>> from zope.schema import Bool
>>> class IBoolThings(IThing1, IThing2):
...     got_that_thing_i_sent_you = Bool(required=True)
>>> @implementer(IBoolThings)
... class BoolThing(object):
...     def __repr__(self): return "<object BoolThing>"validly_providesis a superset ofverifiably_provides:>>> from nti.testing import validly_provides
>>> assert_that(BoolThing(), validly_provides(IThing1, IThing2))
>>> assert_that(BoolThing(), validly_provides(IBoolThings))
Traceback (most recent call last):
...
AssertionError:...
Expected: (object verifiably providing <...IBoolThings> and object validly providing ....IBoolThings)
     but: object verifiably providing <....IBoolThings> Using class <class 'BoolThing'> the object <object BoolThing> has failed to implement interface ....IBoolThings: The ....IBoolThings.got_that_thing_i_sent_you attribute was not provided.
<BLANKLINE>For finer grained control, we can compare data against schema fields
usingvalidated_byandnot_validated_by:>>> from nti.testing import validated_by, not_validated_by
>>> field = IBoolThings.get('got_that_thing_i_sent_you')
>>> assert_that(True, is_(validated_by(field)))
>>> assert_that(None, is_(not_validated_by(field)))Parent/Child RelationshipsTheaq_inContextOfmatcher uses the concepts fromAcquisitionto
check parent/child relationships:>>> from nti.testing import aq_inContextOf
>>> class Parent(object):
...     pass
>>> class Child(object):
...     __parent__ = None
>>> parent = Parent()
>>> child = Child()
>>> child.__parent__ = parent>>> assert_that(child, aq_inContextOf(parent))Test FixturesSupport for test fixtures can be found innti.testing.baseandnti.testing.layers. Thebasepackage includes fully-fleshed
out base classes for direct use, while thelayerspackage mostly includes
mixins that can be used to construct your own test layers.Thebasepackage makes a distinction between “normal” and “shared”
fixtures. Normal fixtures are those that are used for a single test
case. They are established viasetUpand torn down viatearDown.In contrast, shared fixtures are expected to endure for the duration
of all the tests in the class or all the tests in the layer. These are
best used when the fixture is expensive to create. Anything that
extends fromnti.testing.base.AbstractSharedTestBasecreates a shared fixture.
Through the magic of metaclasses, such a subclass can also be assigned
as thelayerproperty of another class to be used as a test layer
that can be shared across more than one class.The most important bases arenti.testing.base.ConfiguringTestBaseandnti.testing.base.SharedConfiguringTestBase. These are both fixtures for
configuring ZCML, either from existing packages or complete file
paths. To use these, subclass them and define class attributesset_up_packagesand (if necessary)features:>>> from nti.testing.base import ConfiguringTestBase
>>> import zope.security
>>> class MyConfiguringTest(ConfiguringTestBase):
...     set_up_packages = (
...         'zope.component', # the default configuration by name
...          # a named file in a named package
...          ('ftesting.zcml', 'zope.traversing.tests'),
...          # an imported module
...          zope.security,
...          # Our own package; in a test, this will mean the parent
...          # package
...          ".")We would then proceed to write our test methods. The packages that we
specified will be set up and torn down around every test method. In
addition, thezope.testingcleanup functions will also run around
every test method.TimeHaving a clock that’s guaranteed to move in a positive increasing way
in every call totime.timeis useful.nti.testing.timeprovides a decorator to accomplish this that ensures values always are
at least the current time and always are increasing. (It is not thread
safe.) It can be applied to functions or methods, and optionally takes
agranularityargument:>>> from nti.testing import time_monotonically_increases
>>> from nti.testing.time import reset_monotonic_time
>>> @time_monotonically_increases(0.1) # increment by 0.1
... def test():
...     import time
...     t1 = time.time()
...     t2 = time.time()
...     assert t2 == t1 + 0.1, (t2, t1)>>> test()And The RestThere are some other assorted utilities, including support for working with
ZODB innti.testing.zodb. See the API documentation for details.Changes4.0.0 (2023-10-24)Add support for Python 3.10, 3.11 and 3.12.Drop support for Python 2 and Python 3.6 and 3.7.Add a layer for working with atestgresPostgres instance.Add methods to the test base classes to supportunittest.mockpatches.3.1.0 (2021-09-08)Add support for Python 3.9.Drop support for Python 3.5.Add the module aliasnti.testing.mock, which is either the
standard libraryunittest.mock, or the backportmock. This
allows easy imports when backwards compatibility matters.Makemock,mock.Mockand various other API attributes,
likeis_true, available directly from thenti.testingnamespace.3.0.0 (2020-06-16)Add support for Python 3.8.Require zope.interface 5.1. This lets the interface matchers produce
much more informative error messages.Addnti.testing.zodbwith helpers for dealing with ZODB. This
makes ZODB 5.6 or above a required dependency.2.2.1 (2019-09-10)Make transaction cleanup safer if the default transaction manager
has been made explicit.Also, reset the default transaction manager to implicit.Seeissue 17.2.2.0 (2018-08-24)Add support for Python 3.7.Maketime_monotonically_increasesalso handletime.gmtimeand add a helper for using it in layers.2.1.0 (2017-10-23)MakeAcquisitionan optional dependency. If it is not installed,
theaq_inContextOfmatcher will always return False.Remove dependency onfudge. Instead, we now useunittest.mockon
Python 3, or its backportmockon Python 2. Seeissue 11.Refactor ZCML configuration support to share more code and
documentation. Seeissue 10.The layerConfiguringLayerMixinand the base classSharedConfiguringTestBasenow default to running
configuration in the package the subclass is defined in, just as
subclasses ofConfiguringTestBasedid.2.0.1 (2017-10-18)The validation matchers (validated_byandnot_validated_by)
now consider it a failure (by default) if the validate method raises
anything other thanzope.interface.exceptions.Invalid(which
includes thezope.schemaexceptions likeWrongType).
Previously, they accepted any exception as meaning the object was
invalid, but this could hide bugs in the actual validation method
itself. You can supply theinvalidargument to the matchers to
loosen or tighten this as desired. (Givinginvalid=Exceptionwill restore the old behaviour.)
Seeissue 7.2.0.0 (2017-04-12)Add support for Python 3.6.Removeunicode_literals.Substantially reworktime_monotonically_increasesfor greater
safety. Fixesissue 5.1.0.0 (2016-07-28)Add Python 3 support.Initial PyPI release.