diff --git a/.travis.yml b/.travis.yml
index dd4c3e0..8b5bd09 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -82,17 +82,22 @@ before_install:
     fi
 
 install:
-  - pip install -U pip setuptools
-  - pip install -U coveralls coverage
-  - pip install -U -e ".[test]"
+  # virtualenv 20.0 together with terryfy macOS is broken, at least
+  # with CPython 2.7.17 (https://github.com/pypa/virtualenv/issues/1555):
+  # it doesn't install or activate the virtualenv correctly and PATH
+  # isn't setup right. So try to use `python -m` to workaround that.
+  - python -m pip install -U pip setuptools
+  - python -m pip install -U coveralls coverage
+  - python -m pip install -U -e ".[test]"
 
 script:
+  - which python
   - python --version
-  - coverage run -m unittest discover -s src
+  - python -m coverage run -m unittest discover -s src
   - python setup.py -q bdist_wheel
 
 after_success:
-  - coveralls
+  - python -m coveralls
   - |
     if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
       # macpython 3.5 doesn't support recent TLS protocols which causes twine
diff --git a/CHANGES.rst b/CHANGES.rst
index b484a99..b21f54d 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -108,6 +108,14 @@
   .. caution:: This will break consumers (such as doctests) that
                depended on the exact error messages.
 
+- Make ``verifyObject`` and ``verifyClass`` report all errors, if the
+  candidate object has multiple detectable violations. Previously they
+  reported only the first error. See `issue
+  <https://github.com/zopefoundation/zope.interface/issues/171>`_.
+
+  Like the above, this will break consumers depending on the exact
+  output of error messages if more than one error is present.
+
 4.7.1 (2019-11-11)
 ==================
 
diff --git a/docs/verify.rst b/docs/verify.rst
index 6f48218..06a0a0f 100644
--- a/docs/verify.rst
+++ b/docs/verify.rst
@@ -14,6 +14,82 @@ Verifying objects
 
 .. autoexception:: zope.interface.Invalid
 
+Let's demonstrate. We'll begin by defining a simple interface hierarchy
+requiring two attributes, and a helper method that will instantiate and verify
+that an object provides this interface.
+
+.. doctest::
+
+   >>> from zope.interface import Interface, Attribute, implementer
+   >>> from zope.interface import Invalid
+   >>> from zope.interface.verify import verifyObject
+   >>> class IBase(Interface):
+   ...     x = Attribute("The X attribute")
+   >>> class IFoo(IBase):
+   ...     y = Attribute("The Y attribute")
+   >>> class Foo(object):
+   ...     pass
+   >>> def verify_foo(**kwargs):
+   ...    foo = Foo()
+   ...    try:
+   ...        return verifyObject(IFoo, foo, **kwargs)
+   ...    except Invalid as e:
+   ...        print(e)
+
+If we try to verify an instance of this ``Foo`` class, three errors
+will be reported. The declarations (does the object provide ``IFoo``)
+are checked, as are the attributes specified in the interface being
+validated (and its ancestors). Notice that the interface being
+verified is shown, as is the interface where the attribute was
+defined.
+
+.. doctest::
+
+   >>> verify_foo()
+   The object <Foo...> has failed to implement interface <...IFoo>:
+       Does not declaratively implement the interface
+       The IBase.x attribute was not provided
+       The IFoo.y attribute was not provided
+
+If we add the two missing attributes, we still have the error about not
+declaring the correct interface.
+
+.. doctest::
+
+   >>> Foo.x = Foo.y = 42
+   >>> verify_foo()
+   The object <Foo...> has failed to implement interface <...IFoo>: Does not declaratively implement the interface.
+
+If we want to only check the structure of the object, without examining
+its declarations, we can use the ``tentative`` argument.
+
+.. doctest::
+
+   >>> verify_foo(tentative=True)
+   True
+
+Of course, we can always mark a particular instance as providing the
+desired interface.
+
+.. doctest::
+
+   >>> from zope.interface import alsoProvides
+   >>> foo = Foo()
+   >>> alsoProvides(foo, IFoo)
+   >>> verifyObject(IFoo, foo)
+   True
+
+If all instances will provide the interface, we can
+mark the class as implementing it.
+
+.. doctest::
+
+   >>> from zope.interface import classImplements
+   >>> classImplements(Foo, IFoo)
+   >>> verify_foo()
+   True
+
+
 Testing for attributes
 ----------------------
 
@@ -22,46 +98,44 @@ Attributes of the object, be they defined by its class or added by its
 
 .. doctest::
 
-   >>> from zope.interface import Interface, Attribute, implementer
-   >>> from zope.interface.exceptions import BrokenImplementation
-   >>> class IFoo(Interface):
-   ...     x = Attribute("The X attribute")
-   ...     y = Attribute("The Y attribute")
-
    >>> @implementer(IFoo)
    ... class Foo(object):
    ...     x = 1
    ...     def __init__(self):
    ...         self.y = 2
 
-   >>> from zope.interface.verify import verifyObject
    >>> verifyObject(IFoo, Foo())
    True
 
 If either attribute is missing, verification will fail by raising an
-exception. (We'll define a helper to make this easier to show.)
+exception.
 
 .. doctest::
 
-   >>> def verify_foo():
-   ...    foo = Foo()
-   ...    try:
-   ...        return verifyObject(IFoo, foo)
-   ...    except BrokenImplementation as e:
-   ...        print(e)
-
    >>> @implementer(IFoo)
    ... class Foo(object):
    ...     x = 1
    >>> verify_foo()
-   The object <Foo...> has failed to implement interface <InterfaceClass ...IFoo>: The IFoo.y attribute was not provided.
+   The object <Foo...> has failed to implement interface <...IFoo>: The IFoo.y attribute was not provided.
    >>> @implementer(IFoo)
    ... class Foo(object):
    ...     def __init__(self):
    ...         self.y = 2
    >>> verify_foo()
-   The object <Foo...> has failed to implement interface <InterfaceClass ...IFoo>: The IFoo.x attribute was not provided.
+   The object <Foo...> has failed to implement interface <...IFoo>: The IBase.x attribute was not provided.
 
+If both attributes are missing, an exception is raised reporting
+both errors.
+
+.. doctest::
+
+    >>> @implementer(IFoo)
+    ... class Foo(object):
+    ...     pass
+    >>> verify_foo()
+    The object <Foo ...> has failed to implement interface <...IFoo>:
+        The IBase.x attribute was not provided
+        The IFoo.y attribute was not provided
 
 If an attribute is implemented as a property that raises an ``AttributeError``
 when trying to get its value, the attribute is considered missing:
@@ -76,7 +150,7 @@ when trying to get its value, the attribute is considered missing:
    ...     def x(self):
    ...         raise AttributeError
    >>> verify_foo()
-   The object <Foo...> has failed to implement interface <InterfaceClass ...IFoo>: The IFoo.x attribute was not provided.
+   The object <Foo...> has failed to implement interface <...IFoo>: The IFoo.x attribute was not provided.
 
 
 Any other exception raised by a property will propagate to the caller of
@@ -122,20 +196,9 @@ that takes one argument. If we don't provide it, we get an error.
    ... class Foo(object):
    ...    pass
    >>> verify_foo()
-   The object <Foo...> has failed to implement interface <InterfaceClass builtins.IFoo>: The IFoo.simple(arg1) attribute was not provided.
-
-Once they exist, they are checked for compatible signatures. This is a
-different type of exception, so we need an updated helper.
-
-.. doctest::
+   The object <Foo...> has failed to implement interface <...IFoo>: The IFoo.simple(arg1) attribute was not provided.
 
-   >>> from zope.interface.exceptions import BrokenMethodImplementation
-   >>> def verify_foo():
-   ...    foo = Foo()
-   ...    try:
-   ...        return verifyObject(IFoo, foo)
-   ...    except BrokenMethodImplementation as e:
-   ...        print(e)
+Once they exist, they are checked to be callable, and for compatible signatures.
 
 Not being callable is an error.
 
@@ -143,24 +206,24 @@ Not being callable is an error.
 
    >>> Foo.simple = 42
    >>> verify_foo()
-   The object <Foo...> violates its contract in IFoo.simple(arg1): implementation is not a method.
+   The object <Foo...> has failed to implement interface <...IFoo>: The contract of IFoo.simple(arg1) is violated because '42' is not a method.
 
-Taking too few arguments is an error.
+Taking too few arguments is an error. (Recall that the ``self``
+argument is implicit.)
 
 .. doctest::
 
-   >>> Foo.simple = lambda: "I take no arguments"
+   >>> Foo.simple = lambda self: "I take no arguments"
    >>> verify_foo()
-   The object <Foo...> violates its contract in IFoo.simple(arg1): implementation doesn't allow enough arguments.
+   The object <Foo...> has failed to implement interface <...IFoo>: The contract of IFoo.simple(arg1) is violated because '<lambda>()' doesn't allow enough arguments.
 
-Requiring too many arguments is an error. (Recall that the ``self``
-argument is implicit.)
+Requiring too many arguments is an error.
 
 .. doctest::
 
    >>> Foo.simple = lambda self, a, b: "I require two arguments"
    >>> verify_foo()
-   The object <Foo...> violates its contract in IFoo.simple(arg1): implementation requires too many arguments.
+   The object <Foo...> has failed to implement interface <...IFoo>: The contract of IFoo.simple(arg1) is violated because '<lambda>(a, b)' requires too many arguments.
 
 Variable arguments can be used to implement the required number, as
 can arguments with defaults.
@@ -185,7 +248,7 @@ variable keyword arguments, the implementation must also accept them.
    ... class Foo(object):
    ...     def needs_kwargs(self, a=1, b=2): pass
    >>> verify_foo()
-   The object <Foo...> violates its contract in IFoo.needs_kwargs(**kwargs): implementation doesn't support keyword arguments.
+   The object <Foo...> has failed to implement interface <...IFoo>: The contract of IFoo.needs_kwargs(**kwargs) is violated because 'Foo.needs_kwargs(a=1, b=2)' doesn't support keyword arguments.
 
    >>> class IFoo(Interface):
    ...    def needs_varargs(*args): pass
@@ -193,15 +256,36 @@ variable keyword arguments, the implementation must also accept them.
    ... class Foo(object):
    ...     def needs_varargs(self, **kwargs): pass
    >>> verify_foo()
-   The object <Foo...> violates its contract in IFoo.needs_varargs(*args): implementation doesn't support variable arguments.
+   The object <Foo...> has failed to implement interface <...IFoo>: The contract of IFoo.needs_varargs(*args) is violated because 'Foo.needs_varargs(**kwargs)' doesn't support variable arguments.
+
+Of course, missing attributes are also found and reported, and the
+source interface of the missing attribute is included. Similarly, when
+the failing method is from a parent class, that is also reported.
+
+.. doctest::
+
+   >>> class IBase(Interface):
+   ...    def method(arg1): "Takes one positional argument"
+   >>> class IFoo(IBase):
+   ...    x = Attribute('The X attribute')
+   >>> class Base(object):
+   ...    def method(self): "I don't have enough arguments"
+   >>> @implementer(IFoo)
+   ... class Foo(Base):
+   ...    pass
+   >>> verify_foo()
+   The object <Foo...> has failed to implement interface <...IFoo>:
+       The contract of IBase.method(arg1) is violated because 'Base.method()' doesn't allow enough arguments
+       The IFoo.x attribute was not provided
 
 Verifying Classes
 =================
 
 The function `verifyClass` is used to check that a class implements
 an interface properly, meaning that its instances properly provide the
-interface. Most of the same things that `verifyObject` checks can be
-checked for classes.
+interface. Many of the same things that `verifyObject` checks can be
+checked for classes, but certain conditions, such as the presence of
+attributes, cannot be verified.
 
 .. autofunction:: verifyClass
 
@@ -211,8 +295,8 @@ checked for classes.
     >>> def verify_foo_class():
     ...    try:
     ...        return verifyClass(IFoo, Foo)
-    ...    except BrokenMethodImplementation as e:
+    ...    except Invalid as e:
     ...        print(e)
 
     >>> verify_foo_class()
-    The object <class 'Foo'> violates its contract in IFoo.needs_varargs(*args): implementation doesn't support variable arguments.
+    The object <class 'Foo'> has failed to implement interface <...IFoo>: The contract of IBase.method(arg1) is violated because 'Base.method(self)' doesn't allow enough arguments.
diff --git a/src/zope/interface/exceptions.py b/src/zope/interface/exceptions.py
index 8d21f33..2f3758b 100644
--- a/src/zope/interface/exceptions.py
+++ b/src/zope/interface/exceptions.py
@@ -20,6 +20,7 @@ __all__ = [
     'DoesNotImplement',
     'BrokenImplementation',
     'BrokenMethodImplementation',
+    'MultipleInvalid',
     # Other
     'BadImplements',
     'InvalidInterface',
@@ -29,19 +30,94 @@ class Invalid(Exception):
     """A specification is violated
     """
 
-_NotGiven = object()
 
-class _TargetMixin(object):
-    target = _NotGiven
+class _TargetInvalid(Invalid):
+    # Internal use. Subclass this when you're describing
+    # a particular target object that's invalid according
+    # to a specific interface.
+    #
+    # For backwards compatibility, the *target* and *interface* are
+    # optional, and the signatures are inconsistent in their ordering.
+    #
+    # We deal with the inconsistency in ordering by defining the index
+    # of the two values in ``self.args``. *target* uses a marker object to
+    # distinguish "not given" from "given, but None", because the latter
+    # can be a value that gets passed to validation. For this reason, it must
+    # always be the last argument (we detect absense by the ``IndexError``).
+
+    _IX_INTERFACE = 0
+    _IX_TARGET = 1
+    # The exception to catch when indexing self.args indicating that
+    # an argument was not given. If all arguments are expected,
+    # a subclass should set this to ().
+    _NOT_GIVEN_CATCH = IndexError
+    _NOT_GIVEN = '<Not Given>'
+
+    def _get_arg_or_default(self, ix, default=None):
+        try:
+            return self.args[ix] # pylint:disable=unsubscriptable-object
+        except self._NOT_GIVEN_CATCH:
+            return default
+
+    @property
+    def interface(self):
+        return self._get_arg_or_default(self._IX_INTERFACE)
+
+    @property
+    def target(self):
+        return self._get_arg_or_default(self._IX_TARGET, self._NOT_GIVEN)
+
+    ###
+    # str
+    #
+    # The ``__str__`` of self is implemented by concatenating (%s), in order,
+    # these properties (none of which should have leading or trailing
+    # whitespace):
+    #
+    # - self._str_subject
+    #   Begin the message, including a description of the target.
+    # - self._str_description
+    #   Provide a general description of the type of error, including
+    #   the interface name if possible and relevant.
+    # - self._str_conjunction
+    #   Join the description to the details. Defaults to ": ".
+    # - self._str_details
+    #   Provide details about how this particular instance of the error.
+    # - self._str_trailer
+    #   End the message. Usually just a period.
+    ###
 
     @property
-    def _prefix(self):
-        if self.target is _NotGiven:
+    def _str_subject(self):
+        target = self.target
+        if target is self._NOT_GIVEN:
             return "An object"
-        return "The object %r" % (self.target,)
+        return "The object %r" % (target,)
+
+    @property
+    def _str_description(self):
+        return "has failed to implement interface %s" % (
+            self.interface or '<Unknown>'
+        )
+
+    _str_conjunction = ": "
+    _str_details = "<unknown>"
+    _str_trailer = '.'
+
+    def __str__(self):
+        return "%s %s%s%s%s" % (
+            self._str_subject,
+            self._str_description,
+            self._str_conjunction,
+            self._str_details,
+            self._str_trailer
+        )
 
-class DoesNotImplement(Invalid, _TargetMixin):
+
+class DoesNotImplement(_TargetInvalid):
     """
+    DoesNotImplement(interface[, target])
+
     The *target* (optional) does not implement the *interface*.
 
     .. versionchanged:: 5.0.0
@@ -49,19 +125,13 @@ class DoesNotImplement(Invalid, _TargetMixin):
        string value of this object accordingly.
     """
 
-    def __init__(self, interface, target=_NotGiven):
-        Invalid.__init__(self)
-        self.interface = interface
-        self.target = target
+    _str_details = "Does not declaratively implement the interface"
 
-    def __str__(self):
-        return "%s does not implement the interface %s." % (
-            self._prefix,
-            self.interface
-        )
 
-class BrokenImplementation(Invalid, _TargetMixin):
+class BrokenImplementation(_TargetInvalid):
     """
+    BrokenImplementation(interface, name[, target])
+
     The *target* (optional) is missing the attribute *name*.
 
     .. versionchanged:: 5.0.0
@@ -71,45 +141,128 @@ class BrokenImplementation(Invalid, _TargetMixin):
        The *name* can either be a simple string or a ``Attribute`` object.
     """
 
-    def __init__(self, interface, name, target=_NotGiven):
-        Invalid.__init__(self)
-        self.interface = interface
-        self.name = name
-        self.target = target
+    _IX_NAME = _TargetInvalid._IX_INTERFACE + 1
+    _IX_TARGET = _IX_NAME + 1
 
-    def __str__(self):
-        return "%s has failed to implement interface %s: The %s attribute was not provided." % (
-            self._prefix,
-            self.interface,
+    @property
+    def name(self):
+        return self.args[1] # pylint:disable=unsubscriptable-object
+
+    @property
+    def _str_details(self):
+        return "The %s attribute was not provided" % (
             repr(self.name) if isinstance(self.name, str) else self.name
         )
 
-class BrokenMethodImplementation(Invalid, _TargetMixin):
+
+class BrokenMethodImplementation(_TargetInvalid):
     """
-    The *target* (optional) has a *method* that violates
+    BrokenMethodImplementation(method, message[, implementation, interface, target])
+
+    The *target* (optional) has a *method* in *implementation* that violates
     its contract in a way described by *mess*.
 
     .. versionchanged:: 5.0.0
-       Add the *target* argument and attribute, and change the resulting
-       string value of this object accordingly.
+       Add the *interface* and *target* argument and attribute,
+       and change the resulting string value of this object accordingly.
 
        The *method* can either be a simple string or a ``Method`` object.
+
+    .. versionchanged:: 5.0.0
+       If *implementation* is given, then the *message* will have the
+       string "implementation" replaced with an short but informative
+       representation of *implementation*.
+
     """
 
-    def __init__(self, method, mess, target=_NotGiven):
-        Invalid.__init__(self)
-        self.method = method
-        self.mess = mess
-        self.target = target
+    _IX_IMPL = 2
+    _IX_INTERFACE = _IX_IMPL + 1
+    _IX_TARGET = _IX_INTERFACE + 1
 
-    def __str__(self):
-        return "%s violates its contract in %s: %s." % (
-            self._prefix,
+    @property
+    def method(self):
+        return self.args[0] # pylint:disable=unsubscriptable-object
+
+    @property
+    def mess(self):
+        return self.args[1] # pylint:disable=unsubscriptable-object
+
+    @staticmethod
+    def __implementation_str(impl):
+        # It could be a callable or some arbitrary object, we don't
+        # know yet.
+        import inspect # Inspect is a heavy-weight dependency, lots of imports
+        try:
+            sig = inspect.signature
+            formatsig = str
+        except AttributeError:
+            sig = inspect.getargspec
+            f = inspect.formatargspec
+            formatsig = lambda sig: f(*sig) # pylint:disable=deprecated-method
+
+        try:
+            sig = sig(impl)
+        except (ValueError, TypeError):
+            # Unable to introspect. Darn.
+            # This could be a non-callable, or a particular builtin,
+            # or a bound method that doesn't even accept 'self', e.g.,
+            # ``Class.method = lambda: None; Class().method``
+            return repr(impl)
+
+        try:
+            name = impl.__qualname__
+        except AttributeError:
+            name = impl.__name__
+
+        return name + formatsig(sig)
+
+    @property
+    def _str_details(self):
+        impl = self._get_arg_or_default(self._IX_IMPL, self._NOT_GIVEN)
+        message = self.mess
+        if impl is not self._NOT_GIVEN and 'implementation' in message:
+            message = message.replace("implementation", '%r')
+            message = message % (self.__implementation_str(impl),)
+
+        return 'The contract of %s is violated because %s' % (
             repr(self.method) if isinstance(self.method, str) else self.method,
-            self.mess
+            message,
         )
 
 
+class MultipleInvalid(_TargetInvalid):
+    """
+    The *target* has failed to implement the *interface* in
+    multiple ways.
+
+    The failures are described by *exceptions*, a collection of
+    other `Invalid` instances.
+
+    .. versionadded:: 5.0
+    """
+
+    _NOT_GIVEN_CATCH = ()
+
+    def __init__(self, interface, target, exceptions):
+        super(MultipleInvalid, self).__init__(interface, target, tuple(exceptions))
+
+    @property
+    def exceptions(self):
+        return self.args[2] # pylint:disable=unsubscriptable-object
+
+    @property
+    def _str_details(self):
+        # It would be nice to use tabs here, but that
+        # is hard to represent in doctests.
+        return '\n    ' + '\n    '.join(
+            x._str_details.strip() if isinstance(x, _TargetInvalid) else str(x)
+            for x in self.exceptions
+        )
+
+    _str_conjunction = ':' # We don't want a trailing space, messes up doctests
+    _str_trailer = ''
+
+
 class InvalidInterface(Exception):
     """The interface has invalid contents
     """
diff --git a/src/zope/interface/verify.py b/src/zope/interface/verify.py
index a8dd952..0a64aeb 100644
--- a/src/zope/interface/verify.py
+++ b/src/zope/interface/verify.py
@@ -21,8 +21,12 @@ from types import MethodType
 
 from zope.interface._compat import PYPY2
 
-from zope.interface.exceptions import BrokenImplementation, DoesNotImplement
+from zope.interface.exceptions import BrokenImplementation
 from zope.interface.exceptions import BrokenMethodImplementation
+from zope.interface.exceptions import DoesNotImplement
+from zope.interface.exceptions import Invalid
+from zope.interface.exceptions import MultipleInvalid
+
 from zope.interface.interface import fromMethod, fromFunction, Method
 
 __all__ = [
@@ -55,8 +59,16 @@ def _verify(iface, candidate, tentative=False, vtype=None):
 
     - Making sure the candidate defines all the necessary attributes
 
+    :return bool: Returns a true value if everything that could be
+       checked passed.
     :raises zope.interface.Invalid: If any of the previous
        conditions does not hold.
+
+    .. versionchanged:: 5.0
+        If multiple methods or attributes are invalid, all such errors
+        are collected and reported. Previously, only the first error was reported.
+        As a special case, if only one such error is present, it is raised
+        alone, like before.
     """
 
     if vtype == 'c':
@@ -64,74 +76,92 @@ def _verify(iface, candidate, tentative=False, vtype=None):
     else:
         tester = iface.providedBy
 
+    excs = []
     if not tentative and not tester(candidate):
-        raise DoesNotImplement(iface)
+        excs.append(DoesNotImplement(iface, candidate))
 
-    # Here the `desc` is either an `Attribute` or `Method` instance
     for name, desc in iface.namesAndDescriptions(all=True):
         try:
-            attr = getattr(candidate, name)
-        except AttributeError:
-            if (not isinstance(desc, Method)) and vtype == 'c':
-                # We can't verify non-methods on classes, since the
-                # class may provide attrs in it's __init__.
-                continue
-
-            raise BrokenImplementation(iface, desc, candidate)
-
-        if not isinstance(desc, Method):
-            # If it's not a method, there's nothing else we can test
-            continue
-
-        if inspect.ismethoddescriptor(attr) or inspect.isbuiltin(attr):
-            # The first case is what you get for things like ``dict.pop``
-            # on CPython (e.g., ``verifyClass(IFullMapping, dict))``). The
-            # second case is what you get for things like ``dict().pop`` on
-            # CPython (e.g., ``verifyObject(IFullMapping, dict()))``.
-            # In neither case can we get a signature, so there's nothing
-            # to verify. Even the inspect module gives up and raises
-            # ValueError: no signature found. The ``__text_signature__`` attribute
-            # isn't typically populated either.
-            #
-            # Note that on PyPy 2 or 3 (up through 7.3 at least), these are
-            # not true for things like ``dict.pop`` (but might be true for C extensions?)
-            continue
-
-        if isinstance(attr, FunctionType):
-            if sys.version_info[0] >= 3 and isinstance(candidate, type) and vtype == 'c':
-                # This is an "unbound method" in Python 3.
-                # Only unwrap this if we're verifying implementedBy;
-                # otherwise we can unwrap @staticmethod on classes that directly
-                # provide an interface.
-                meth = fromFunction(attr, iface, name=name,
-                                    imlevel=1)
-            else:
-                # Nope, just a normal function
-                meth = fromFunction(attr, iface, name=name)
-        elif (isinstance(attr, MethodTypes)
-              and type(attr.__func__) is FunctionType):
-            meth = fromMethod(attr, iface, name)
-        elif isinstance(attr, property) and vtype == 'c':
-            # We without an instance we cannot be sure it's not a
-            # callable.
-            continue
-        else:
-            if not callable(attr):
-                raise BrokenMethodImplementation(desc, "implementation is not a method", candidate)
-            # sigh, it's callable, but we don't know how to introspect it, so
-            # we have to give it a pass.
-            continue
-
-        # Make sure that the required and implemented method signatures are
-        # the same.
-        mess = _incompat(desc.getSignatureInfo(), meth.getSignatureInfo())
-        if mess:
-            if PYPY2 and _pypy2_false_positive(mess, candidate, vtype):
-                continue
-            raise BrokenMethodImplementation(desc, mess, candidate)
+            _verify_element(iface, name, desc, candidate, vtype)
+        except Invalid as e:
+            excs.append(e)
+
+    if excs:
+        if len(excs) == 1:
+            raise excs[0]
+        raise MultipleInvalid(iface, candidate, excs)
 
     return True
 
+def _verify_element(iface, name, desc, candidate, vtype):
+    # Here the `desc` is either an `Attribute` or `Method` instance
+    try:
+        attr = getattr(candidate, name)
+    except AttributeError:
+        if (not isinstance(desc, Method)) and vtype == 'c':
+            # We can't verify non-methods on classes, since the
+            # class may provide attrs in it's __init__.
+            return
+        # TODO: On Python 3, this should use ``raise...from``
+        raise BrokenImplementation(iface, desc, candidate)
+
+    if not isinstance(desc, Method):
+        # If it's not a method, there's nothing else we can test
+        return
+
+    if inspect.ismethoddescriptor(attr) or inspect.isbuiltin(attr):
+        # The first case is what you get for things like ``dict.pop``
+        # on CPython (e.g., ``verifyClass(IFullMapping, dict))``). The
+        # second case is what you get for things like ``dict().pop`` on
+        # CPython (e.g., ``verifyObject(IFullMapping, dict()))``.
+        # In neither case can we get a signature, so there's nothing
+        # to verify. Even the inspect module gives up and raises
+        # ValueError: no signature found. The ``__text_signature__`` attribute
+        # isn't typically populated either.
+        #
+        # Note that on PyPy 2 or 3 (up through 7.3 at least), these are
+        # not true for things like ``dict.pop`` (but might be true for C extensions?)
+        return
+
+    if isinstance(attr, FunctionType):
+        if sys.version_info[0] >= 3 and isinstance(candidate, type) and vtype == 'c':
+            # This is an "unbound method" in Python 3.
+            # Only unwrap this if we're verifying implementedBy;
+            # otherwise we can unwrap @staticmethod on classes that directly
+            # provide an interface.
+            meth = fromFunction(attr, iface, name=name,
+                                imlevel=1)
+        else:
+            # Nope, just a normal function
+            meth = fromFunction(attr, iface, name=name)
+    elif (isinstance(attr, MethodTypes)
+          and type(attr.__func__) is FunctionType):
+        meth = fromMethod(attr, iface, name)
+    elif isinstance(attr, property) and vtype == 'c':
+        # Without an instance we cannot be sure it's not a
+        # callable.
+        # TODO: This should probably check inspect.isdatadescriptor(),
+        # a more general form than ``property``
+        return
+
+    else:
+        if not callable(attr):
+            raise BrokenMethodImplementation(desc, "implementation is not a method",
+                                             attr, iface, candidate)
+        # sigh, it's callable, but we don't know how to introspect it, so
+        # we have to give it a pass.
+        return
+
+    # Make sure that the required and implemented method signatures are
+    # the same.
+    mess = _incompat(desc.getSignatureInfo(), meth.getSignatureInfo())
+    if mess:
+        if PYPY2 and _pypy2_false_positive(mess, candidate, vtype):
+            return
+        raise BrokenMethodImplementation(desc, mess, attr, iface, candidate)
+
+
+
 def verifyClass(iface, candidate, tentative=False):
     """
     Verify that the *candidate* might correctly provide *iface*.

verify[Object|Class] should report all errors found
It's painful to work through errors one at a time when all errors could be reported at once.
diff --git a/src/zope/interface/tests/test_exceptions.py b/src/zope/interface/tests/test_exceptions.py
index 34ee071..a55f522 100644
--- a/src/zope/interface/tests/test_exceptions.py
+++ b/src/zope/interface/tests/test_exceptions.py
@@ -35,15 +35,19 @@ class DoesNotImplementTests(unittest.TestCase):
         dni = self._makeOne()
         self.assertEqual(
             str(dni),
-            'An object does not implement the interface '
-            '<InterfaceClass zope.interface.tests.test_exceptions.IDummy>.')
+            "An object has failed to implement interface "
+            "<InterfaceClass zope.interface.tests.test_exceptions.IDummy>: "
+            "Does not declaratively implement the interface."
+        )
 
     def test___str__w_candidate(self):
         dni = self._makeOne('candidate')
         self.assertEqual(
             str(dni),
-            'The object \'candidate\' does not implement the interface '
-            '<InterfaceClass zope.interface.tests.test_exceptions.IDummy>.')
+            "The object 'candidate' has failed to implement interface "
+            "<InterfaceClass zope.interface.tests.test_exceptions.IDummy>: "
+            "Does not declaratively implement the interface."
+        )
 
 
 class BrokenImplementationTests(unittest.TestCase):
@@ -72,23 +76,109 @@ class BrokenImplementationTests(unittest.TestCase):
             '<InterfaceClass zope.interface.tests.test_exceptions.IDummy>: '
             "The 'missing' attribute was not provided.")
 
+
+def broken_function():
+    """
+    This is a global function with a simple argument list.
+
+    It exists to be able to report the same information when
+    formatting signatures under Python 2 and Python 3.
+    """
+
+
 class BrokenMethodImplementationTests(unittest.TestCase):
 
     def _getTargetClass(self):
         from zope.interface.exceptions import BrokenMethodImplementation
         return BrokenMethodImplementation
 
+    message = 'I said so'
+
     def _makeOne(self, *args):
-        return self._getTargetClass()('aMethod', 'I said so', *args)
+        return self._getTargetClass()('aMethod', self.message, *args)
 
     def test___str__(self):
         dni = self._makeOne()
         self.assertEqual(
             str(dni),
-            "An object violates its contract in 'aMethod': I said so.")
+            "An object has failed to implement interface <Unknown>: "
+            "The contract of 'aMethod' is violated because I said so."
+        )
 
-    def test___str__w_candidate(self):
-        dni = self._makeOne('candidate')
+    def test___str__w_candidate_no_implementation(self):
+        dni = self._makeOne('some_function', '<IFoo>', 'candidate')
         self.assertEqual(
             str(dni),
-            "The object 'candidate' violates its contract in 'aMethod': I said so.")
+            "The object 'candidate' has failed to implement interface <IFoo>: "
+            "The contract of 'aMethod' is violated because I said so."
+        )
+
+    def test___str__w_candidate_w_implementation(self):
+        self.message = 'implementation is wonky'
+        dni = self._makeOne(broken_function, '<IFoo>', 'candidate')
+        self.assertEqual(
+            str(dni),
+            "The object 'candidate' has failed to implement interface <IFoo>: "
+            "The contract of 'aMethod' is violated because "
+            "'broken_function()' is wonky."
+        )
+
+    def test___str__w_candidate_w_implementation_not_callable(self):
+        self.message = 'implementation is not callable'
+        dni = self._makeOne(42, '<IFoo>', 'candidate')
+        self.assertEqual(
+            str(dni),
+            "The object 'candidate' has failed to implement interface <IFoo>: "
+            "The contract of 'aMethod' is violated because "
+            "'42' is not callable."
+        )
+
+    def test___repr__w_candidate(self):
+        dni = self._makeOne(None, 'candidate')
+        self.assertEqual(
+            repr(dni),
+            "BrokenMethodImplementation('aMethod', 'I said so', None, 'candidate')"
+        )
+
+
+class MultipleInvalidTests(unittest.TestCase):
+
+    def _getTargetClass(self):
+        from zope.interface.exceptions import MultipleInvalid
+        return MultipleInvalid
+
+    def _makeOne(self, excs):
+        iface = _makeIface()
+        return self._getTargetClass()(iface, 'target', excs)
+
+    def test__str__(self):
+        from zope.interface.exceptions import BrokenMethodImplementation
+        excs = [
+            BrokenMethodImplementation('aMethod', 'I said so'),
+            Exception("Regular exception")
+        ]
+        dni = self._makeOne(excs)
+        self.assertEqual(
+            str(dni),
+            "The object 'target' has failed to implement interface "
+            "<InterfaceClass zope.interface.tests.test_exceptions.IDummy>:\n"
+            "    The contract of 'aMethod' is violated because I said so\n"
+            "    Regular exception"
+        )
+
+    def test__repr__(self):
+        from zope.interface.exceptions import BrokenMethodImplementation
+        excs = [
+            BrokenMethodImplementation('aMethod', 'I said so'),
+            # Use multiple arguments to normalize repr; versions of Python
+            # prior to 3.7 add a trailing comma if there's just one.
+            Exception("Regular", "exception")
+        ]
+        dni = self._makeOne(excs)
+        self.assertEqual(
+            repr(dni),
+            "MultipleInvalid(<InterfaceClass zope.interface.tests.test_exceptions.IDummy>,"
+            " 'target',"
+            " (BrokenMethodImplementation('aMethod', 'I said so'),"
+            " Exception('Regular', 'exception')))"
+        )
diff --git a/src/zope/interface/tests/test_verify.py b/src/zope/interface/tests/test_verify.py
index 65e390a..3a68d20 100644
--- a/src/zope/interface/tests/test_verify.py
+++ b/src/zope/interface/tests/test_verify.py
@@ -554,6 +554,39 @@ class Test_verifyClass(unittest.TestCase):
         self._callFUT(IReadSequence, tuple, tentative=True)
 
 
+    def test_multiple_invalid(self):
+        from zope.interface.exceptions import MultipleInvalid
+        from zope.interface.exceptions import DoesNotImplement
+        from zope.interface.exceptions import BrokenImplementation
+        from zope.interface import Interface
+        from zope.interface import classImplements
+
+        class ISeveralMethods(Interface):
+            def meth1(arg1):
+                "Method 1"
+            def meth2(arg1):
+                "Method 2"
+
+        class SeveralMethods(object):
+            pass
+
+        with self.assertRaises(MultipleInvalid) as exc:
+            self._callFUT(ISeveralMethods, SeveralMethods)
+
+        ex = exc.exception
+        self.assertEqual(3, len(ex.exceptions))
+        self.assertIsInstance(ex.exceptions[0], DoesNotImplement)
+        self.assertIsInstance(ex.exceptions[1], BrokenImplementation)
+        self.assertIsInstance(ex.exceptions[2], BrokenImplementation)
+
+        # If everything else is correct, only the single error is raised without
+        # the wrapper.
+        classImplements(SeveralMethods, ISeveralMethods)
+        SeveralMethods.meth1 = lambda self, arg1: "Hi"
+
+        with self.assertRaises(BrokenImplementation):
+            self._callFUT(ISeveralMethods, SeveralMethods)
+
 class Test_verifyObject(Test_verifyClass):
 
     @classmethod

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest coverage
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
coverage==7.8.0
exceptiongroup==1.2.2
iniconfig==2.1.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
tomli==2.2.1
-e git+https://github.com/zopefoundation/zope.interface.git@d6343eeaa7f67838db9de82974898b80938a7aef#egg=zope.interface

name: zope.interface
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
  - ca-certificates=2025.2.25=h06a4308_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=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - tomli==2.2.1
prefix: /opt/conda/envs/zope.interface

src/zope/interface/tests/test_exceptions.py::DoesNotImplementTests::test___str__ src/zope/interface/tests/test_exceptions.py::DoesNotImplementTests::test___str__w_candidate src/zope/interface/tests/test_exceptions.py::BrokenMethodImplementationTests::test___repr__w_candidate src/zope/interface/tests/test_exceptions.py::BrokenMethodImplementationTests::test___str__ src/zope/interface/tests/test_exceptions.py::BrokenMethodImplementationTests::test___str__w_candidate_no_implementation src/zope/interface/tests/test_exceptions.py::BrokenMethodImplementationTests::test___str__w_candidate_w_implementation src/zope/interface/tests/test_exceptions.py::BrokenMethodImplementationTests::test___str__w_candidate_w_implementation_not_callable src/zope/interface/tests/test_exceptions.py::MultipleInvalidTests::test__repr__ src/zope/interface/tests/test_exceptions.py::MultipleInvalidTests::test__str__ src/zope/interface/tests/test_verify.py::Test_verifyClass::test_multiple_invalid src/zope/interface/tests/test_verify.py::Test_verifyObject::test_multiple_invalid
src/zope/interface/tests/test_exceptions.py::BrokenImplementationTests::test___str__ src/zope/interface/tests/test_exceptions.py::BrokenImplementationTests::test___str__w_candidate src/zope/interface/tests/test_verify.py::Test_verifyClass::test_class_doesnt_have_required_method_derived src/zope/interface/tests/test_verify.py::Test_verifyClass::test_class_doesnt_have_required_method_simple src/zope/interface/tests/test_verify.py::Test_verifyClass::test_class_doesnt_implement src/zope/interface/tests/test_verify.py::Test_verifyClass::test_class_doesnt_implement_but_classImplements_later src/zope/interface/tests/test_verify.py::Test_verifyClass::test_class_has_attribute_for_attribute src/zope/interface/tests/test_verify.py::Test_verifyClass::test_class_has_method_for_iface_attr src/zope/interface/tests/test_verify.py::Test_verifyClass::test_class_has_nonmethod_for_method src/zope/interface/tests/test_verify.py::Test_verifyClass::test_class_has_required_method_derived src/zope/interface/tests/test_verify.py::Test_verifyClass::test_class_has_required_method_simple src/zope/interface/tests/test_verify.py::Test_verifyClass::test_class_misses_attribute_for_attribute src/zope/interface/tests/test_verify.py::Test_verifyClass::test_dict_IFullMapping src/zope/interface/tests/test_verify.py::Test_verifyClass::test_list_ISequence src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_doesnt_take_required_kwargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_doesnt_take_required_only_kwargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_doesnt_take_required_positional_and_starargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_doesnt_take_required_starargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_extra_arg src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_extra_arg_with_default src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_extra_starargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_extra_starargs_and_kwargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_not_enough_args src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_only_kwargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_only_positional_args src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_only_starargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_positional_plus_required_starargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_required_kwargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_required_positional_and_starargs src/zope/interface/tests/test_verify.py::Test_verifyClass::test_method_takes_wrong_arg_names_but_OK src/zope/interface/tests/test_verify.py::Test_verifyClass::test_tuple_IReadSequence src/zope/interface/tests/test_verify.py::Test_verifyClass::test_w_callable_non_func_method src/zope/interface/tests/test_verify.py::Test_verifyClass::test_w_decorated_method src/zope/interface/tests/test_verify.py::Test_verifyObject::test_class_doesnt_have_required_method_derived src/zope/interface/tests/test_verify.py::Test_verifyObject::test_class_doesnt_have_required_method_simple src/zope/interface/tests/test_verify.py::Test_verifyObject::test_class_doesnt_implement src/zope/interface/tests/test_verify.py::Test_verifyObject::test_class_doesnt_implement_but_classImplements_later src/zope/interface/tests/test_verify.py::Test_verifyObject::test_class_has_attribute_for_attribute src/zope/interface/tests/test_verify.py::Test_verifyObject::test_class_has_method_for_iface_attr src/zope/interface/tests/test_verify.py::Test_verifyObject::test_class_has_nonmethod_for_method src/zope/interface/tests/test_verify.py::Test_verifyObject::test_class_has_required_method_derived src/zope/interface/tests/test_verify.py::Test_verifyObject::test_class_has_required_method_simple src/zope/interface/tests/test_verify.py::Test_verifyObject::test_class_misses_attribute_for_attribute src/zope/interface/tests/test_verify.py::Test_verifyObject::test_dict_IFullMapping src/zope/interface/tests/test_verify.py::Test_verifyObject::test_list_ISequence src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_doesnt_take_required_kwargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_doesnt_take_required_only_kwargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_doesnt_take_required_positional_and_starargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_doesnt_take_required_starargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_extra_arg src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_extra_arg_with_default src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_extra_starargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_extra_starargs_and_kwargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_not_enough_args src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_only_kwargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_only_positional_args src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_only_starargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_positional_plus_required_starargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_required_kwargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_required_positional_and_starargs src/zope/interface/tests/test_verify.py::Test_verifyObject::test_method_takes_wrong_arg_names_but_OK src/zope/interface/tests/test_verify.py::Test_verifyObject::test_module_hit src/zope/interface/tests/test_verify.py::Test_verifyObject::test_module_miss src/zope/interface/tests/test_verify.py::Test_verifyObject::test_staticmethod_hit_on_class src/zope/interface/tests/test_verify.py::Test_verifyObject::test_tuple_IReadSequence src/zope/interface/tests/test_verify.py::Test_verifyObject::test_w_callable_non_func_method src/zope/interface/tests/test_verify.py::Test_verifyObject::test_w_decorated_method
src/zope/interface/exceptions.py src/zope/interface/verify.py .travis.yml CHANGES.rst docs/verify.rst
src/zope/interface/exceptions.py src/zope/interface/verify.py .travis.yml CHANGES.rst docs/verify.rst