diff --git a/CHANGES.rst b/CHANGES.rst
index ae85acd..89282ad 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,14 +1,16 @@
-Changes
-=======
+=========
+ Changes
+=========
 
 2.1.1 (unreleased)
-------------------
+==================
 
 - Add support for Python 3.7.
 
+- Drop support for ``setup.py test``.
 
 2.1.0 (2017-07-25)
-------------------
+==================
 
 - Add support for Python 3.5 and 3.6.
 
@@ -16,7 +18,7 @@ Changes
 
 
 2.0.0 (2014-12-24)
-------------------
+==================
 
 - Added support for PyPy.  (PyPy3 is pending release of a fix for:
   https://bitbucket.org/pypy/pypy/issue/1946)
@@ -27,7 +29,7 @@ Changes
 
 
 2.0.0a1 (2013-02-25)
---------------------
+====================
 
 - Add support for Python 3.3.
 
@@ -44,7 +46,7 @@ Changes
 
 
 1.0.1 (2010-09-25)
-------------------
+==================
 
 - Add undeclared but needed dependency on ``zope.component``.
 
@@ -52,7 +54,7 @@ Changes
 
 
 1.0 (2009-05-19)
-----------------
+================
 
 * Initial public release, derived from zope.app.component and
   zope.app.interface to replace them.
diff --git a/setup.py b/setup.py
index 17cf9cb..175f21c 100644
--- a/setup.py
+++ b/setup.py
@@ -20,20 +20,6 @@ def read(*rnames):
     with open(os.path.join(os.path.dirname(__file__), *rnames)) as f:
         return f.read()
 
-def alltests():
-    import sys
-    import unittest
-    # use the zope.testrunner machinery to find all the
-    # test suites we've put under ourselves
-    import zope.testrunner.find
-    import zope.testrunner.options
-    here = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src'))
-    args = sys.argv[:]
-    defaults = ["--test-path", here]
-    options = zope.testrunner.options.get_options(args, defaults)
-    suites = list(zope.testrunner.find.find_suites(options))
-    return unittest.TestSuite(suites)
-
 TEST_REQUIRES = [
     'zope.configuration',
     'zope.testing',
@@ -89,7 +75,6 @@ setup(name='zope.componentvocabulary',
           'test': TEST_REQUIRES,
       },
       tests_require=TEST_REQUIRES,
-      test_suite='__main__.alltests',
       include_package_data=True,
       zip_safe=False,
 )
diff --git a/src/zope/componentvocabulary/vocabulary.py b/src/zope/componentvocabulary/vocabulary.py
index 329548f..311911d 100644
--- a/src/zope/componentvocabulary/vocabulary.py
+++ b/src/zope/componentvocabulary/vocabulary.py
@@ -20,9 +20,9 @@ import base64
 import six
 import zope.component
 from zope.component.interface import interfaceToName
-from zope.component.interfaces import IUtilityRegistration
 from zope.interface import implementer, provider, Interface, providedBy
 from zope.interface.interfaces import IInterface
+from zope.interface.interfaces import IUtilityRegistration
 from zope.security.proxy import removeSecurityProxy
 from zope.schema.interfaces import IVocabularyTokenized
 from zope.schema.interfaces import ITokenizedTerm, ITitledTokenizedTerm
@@ -287,7 +287,7 @@ class UtilityComponentInterfacesVocabulary(ObjectInterfacesVocabulary):
 
 
 @implementer(ITitledTokenizedTerm)
-class UtilityNameTerm:
+class UtilityNameTerm(object):
     r"""Simple term that provides a utility name as a value.
 
     >>> t1 = UtilityNameTerm('abc')
@@ -338,7 +338,7 @@ class UtilityNameTerm:
 
 
 @implementer(IVocabularyTokenized)
-class UtilityNames:
+class UtilityNames(object):
     """Vocabulary with utility names for a single interface as values.
 
     >>> class IMyUtility(Interface):
@@ -380,6 +380,15 @@ class UtilityNames:
     >>> u'three' in vocab
     True
 
+    If the term is not found, a ValueError is raised from ``getTerm``
+
+    >>> u'four' in vocab
+    False
+    >>> vocab.getTerm(u'four')
+    Traceback (most recent call last):
+    ...
+    ValueError: four
+
     >>> component.provideUtility(MyUtility(), IMyUtility)
     >>> u'' in vocab
     True
@@ -394,6 +403,14 @@ class UtilityNames:
     >>> term3.value
     u'one'
 
+    If we ask ``getTermByToken`` to find a missing token, a
+    ``LookupError`` is raised:
+
+    >>> vocab.getTermByToken(u'no such term')
+    Traceback (most recent call last):
+    ...
+    LookupError: no matching token: 'no such term'
+
     >>> ps.tearDown()
     """
 
diff --git a/tox.ini b/tox.ini
index 4978f79..5996d47 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,14 +5,16 @@ envlist =
 [testenv]
 usedevelop = true
 commands =
-    python setup.py -q test -q
+    zope-testrunner --test-path=src
 deps =
     .[test]
 
 [testenv:coverage]
+basepython =
+    python3.6
 commands =
-    coverage run setup.py -q test -q
-    coverage report
+    coverage run -m zope.testrunner --test-path=src
+    coverage report --fail-under=100
 deps =
     {[testenv]deps}
     coverage

Fix DeprecationWarnings
```
zope/componentvocabulary/vocabulary.py:23: DeprecationWarning: IUtilityRegistration is deprecated. Import from zope.interface.interfaces
  from zope.component.interfaces import IUtilityRegistration
```
diff --git a/src/zope/componentvocabulary/tests/test_configure.py b/src/zope/componentvocabulary/tests/test_configure.py
index c7a30e9..b930057 100644
--- a/src/zope/componentvocabulary/tests/test_configure.py
+++ b/src/zope/componentvocabulary/tests/test_configure.py
@@ -21,11 +21,11 @@ import zope.configuration.xmlconfig
 class ZCMLTest(unittest.TestCase):
 
     def test_configure_zcml_should_be_loadable(self):
-        try:
-            zope.configuration.xmlconfig.XMLConfig(
+        # If this raises, let it be a test error.
+        # We get a more clear exception than if we catch
+        # what it raises and do self.fail(ex)
+        zope.configuration.xmlconfig.XMLConfig(
                 'configure.zcml', zope.componentvocabulary)()
-        except Exception as e:
-            self.fail(e)
 
     def test_configure_should_register_n_utilities(self):
         gsm = zope.component.getGlobalSiteManager()
diff --git a/src/zope/componentvocabulary/tests/test_vocabulary.py b/src/zope/componentvocabulary/tests/test_vocabulary.py
index 6ababce..93ea143 100644
--- a/src/zope/componentvocabulary/tests/test_vocabulary.py
+++ b/src/zope/componentvocabulary/tests/test_vocabulary.py
@@ -16,8 +16,51 @@
 __docformat__ = "reStructuredText"
 import doctest
 import re
+import unittest
+
 from zope.testing import renormalizing
 
+
+class TestUtilityComponentInterfacesVocabulary(unittest.TestCase):
+
+    def _getTargetClass(self):
+        from zope.componentvocabulary.vocabulary import UtilityComponentInterfacesVocabulary
+        return UtilityComponentInterfacesVocabulary
+
+    def _makeOne(self, context):
+        return self._getTargetClass()(context)
+
+    def test_construct_without_registration(self):
+        context = object()
+        vocab = self._makeOne(context)
+
+        self.assertIsNotNone(vocab.getTermByToken('zope.interface.Interface'))
+
+    def test_construct_with_registration_unwraps(self):
+        from zope.interface import Interface
+        from zope.interface import implementer
+        from zope.interface.interfaces import IUtilityRegistration
+
+        @implementer(IUtilityRegistration)
+        class Reg(object):
+            def __init__(self, component):
+                self.component = component
+
+        class IComponent(Interface):
+            "A component interface"
+
+        @implementer(IComponent)
+        class Component(object):
+            "A component"
+
+
+        reg = Reg(Component())
+
+        vocab = self._makeOne(reg)
+        self.assertIsNotNone(
+            vocab.getTermByToken('zope.componentvocabulary.tests.test_vocabulary.IComponent'))
+
+
 checker = renormalizing.RENormalizing([
     # Python 3 unicode removed the "u".
     (re.compile("u('.*?')"),
@@ -28,5 +71,9 @@ checker = renormalizing.RENormalizing([
 
 
 def test_suite():
-    return doctest.DocTestSuite(
-        'zope.componentvocabulary.vocabulary', checker=checker)
+    suite = unittest.defaultTestLoader.loadTestsFromName(__name__)
+    suite.addTest(
+        doctest.DocTestSuite(
+            'zope.componentvocabulary.vocabulary', checker=checker)
+    )
+    return suite

pip install -e .[test]
parse_log_pytest
pytest
pytest pytest-cov
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///croot/attrs_1668696182826/work
certifi @ file:///croot/certifi_1671487769961/work/certifi
coverage==7.2.7
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1648562407465/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
packaging @ file:///croot/packaging_1671697413597/work
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pytest==7.1.2
pytest-cov==4.1.0
six==1.17.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typing_extensions @ file:///croot/typing_extensions_1669924550328/work
zipp @ file:///croot/zipp_1672387121353/work
zope.component==6.0
-e git+https://github.com/zopefoundation/zope.componentvocabulary.git@b876ffc5b172710f976bc49aad58998302720998#egg=zope.componentvocabulary
zope.configuration==5.0.1
zope.event==5.0
zope.exceptions==5.1
zope.hookable==6.0
zope.i18nmessageid==6.1.0
zope.interface==6.4.post2
zope.location==5.0
zope.proxy==5.3
zope.schema==7.0.1
zope.security==6.2
zope.testing==5.0.1
zope.testrunner==6.5

name: zope.componentvocabulary
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib-metadata=4.11.3=py37h06a4308_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - typing_extensions=4.4.0=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.2.7
      - pytest-cov==4.1.0
      - six==1.17.0
      - zope-component==6.0
      - zope-configuration==5.0.1
      - zope-event==5.0
      - zope-exceptions==5.1
      - zope-hookable==6.0
      - zope-i18nmessageid==6.1.0
      - zope-interface==6.4.post2
      - zope-location==5.0
      - zope-proxy==5.3
      - zope-schema==7.0.1
      - zope-security==6.2
      - zope-testing==5.0.1
      - zope-testrunner==6.5
prefix: /opt/conda/envs/zope.componentvocabulary

src/zope/componentvocabulary/tests/test_configure.py::ZCMLTest::test_configure_should_register_n_utilities src/zope/componentvocabulary/tests/test_vocabulary.py::TestUtilityComponentInterfacesVocabulary::test_construct_with_registration_unwraps src/zope/componentvocabulary/tests/test_vocabulary.py::TestUtilityComponentInterfacesVocabulary::test_construct_without_registration src/zope/componentvocabulary/tests/test_vocabulary.py::test_suite
src/zope/componentvocabulary/tests/test_configure.py::ZCMLTest::test_configure_zcml_should_be_loadable
setup.py src/zope/componentvocabulary/vocabulary.py tox.ini CHANGES.rst
setup.py src/zope/componentvocabulary/vocabulary.py tox.ini CHANGES.rst