Repository: marshmallow-code/marshmallow

Issue:
Remove validation on serialization
When using `Dict(values=Nested())` I expect errors in dictionary values to be nested in such a way that I can locate exactly what element is causing the problem. This seems to work as expected for `load()`, but not for `dump()`.

Minimal example:

- Marshmallow version: 3.0.0rc4
- Python version: 3.7.2

```python
from marshmallow import Schema, ValidationError, fields


class InnerSchema(Schema):
    x = fields.Int()


class OuterSchema(Schema):
    inner = fields.Dict(keys=fields.Str(), values=fields.Nested(InnerSchema))


foo = {"inner": {"some-key": {"x": "abc"}}}

try:
    OuterSchema().load(foo)
except ValidationError as e:
    print(e)
    # >>> {'inner': defaultdict(<class 'dict'>, {'some-key': {'value': {'x': ['Not a valid integer.']}}})}
    # Very helpful - and what I expect

class Inner:
    def __init__(self, x):
        self.x = x


class Outer:
    def __init__(self, inner):
        self.inner = inner


bar = Outer(inner={"some-key": Inner(x="abc")})

try:
    OuterSchema().dump(bar)
except ValidationError as e:
    print(e)
    # >>> {'inner': {'x': ['Not a valid integer.']}}
    # Surprisingly - 'some-key' and 'value' are not present here,
    # which makes it harder to pinpoint the problem in large objects
```

Discussion:
lafrech: +32 −454 :heart_eyes:
That's just great.
sloria: Speeds up `tox -e benchmark` by ~15%.
sloria: Once https://github.com/deckar01/marshmallow/pull/1 is merged in and this is brought up to date with `dev`, I think this is good to merge.

We can probably get rid of `ErrorStore` at this point, but that can happen in a follow-up PR.
sloria: Feel free to squash my commits into yours if you want; I don't care about the git creds =).
sloria: @deckar01 Are you working on this, or should I pick up where you left off?
deckar01: I haven't made any changes locally that haven't been pushed. Feel free to pick it up and push on it. I think some tests started failing after merging deckar01#1. Not sure if it was that or the merge conflicts I resolved. There are also new merge conflicts I haven't looked into.
sloria: OK, I can look into the conflicts this week. Mind if I squash and merge after all is said and done?

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 5a0a27a4..2512ff20 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,8 @@ Changelog
 
 Features:
 
+- *Backwards-incompatible*: Validation does not occur on serialization (:issue:`1132`).
+  This significantly improves serialization performance.
 - *Backwards-incompatible*: ``DateTime`` does not affect timezone information
   on serialization and deserialization (:issue:`1234`, :pr:`1287`).
 - Add ``NaiveDateTime`` and ``AwareDateTime`` to enforce timezone awareness
diff --git a/docs/extending.rst b/docs/extending.rst
index 067415f9..07e88307 100644
--- a/docs/extending.rst
+++ b/docs/extending.rst
@@ -216,9 +216,9 @@ The pipeline for serialization is similar, except that the "pass_many" processor
 Handling Errors
 ---------------
 
-By default, :meth:`Schema.dump` and :meth:`Schema.load` will raise a :exc:`ValidationError <marshmallow.exceptions.ValidationError>`.
+By default, :meth:`Schema.load` will raise a :exc:`ValidationError <marshmallow.exceptions.ValidationError>` if passed invalid data.
 
-You can specify a custom error-handling function for a :class:`Schema` by overriding the `handle_error <marshmallow.Schema.handle_error>`  method. The method receives the :exc:`ValidationError <marshmallow.exceptions.ValidationError>` and the original object (or input data if deserializing) to be (de)serialized.
+You can specify a custom error-handling function for a :class:`Schema` by overriding the `handle_error <marshmallow.Schema.handle_error>`  method. The method receives the :exc:`ValidationError <marshmallow.exceptions.ValidationError>` and the original input data to be deserialized.
 
 .. code-block:: python
 
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 605c0ab3..8eb14628 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -45,7 +45,7 @@ Create a schema by defining a class with variables mapping attribute names to :c
 Serializing Objects ("Dumping")
 -------------------------------
 
-Serialize objects by passing them to your schema's :meth:`dump <marshmallow.Schema.dump>` method, which returns the formatted result (or raises a :exc:`ValidationError <marshmallow.exceptions.ValidationError>` with a dictionary of validation errors, which we'll :ref:`revisit later <validation>`).
+Serialize objects by passing them to your schema's :meth:`dump <marshmallow.Schema.dump>` method, which returns the formatted result.
 
 .. code-block:: python
 
@@ -84,9 +84,11 @@ You can also exclude fields by passing in the ``exclude`` parameter.
 Deserializing Objects ("Loading")
 ---------------------------------
 
-The opposite of the :meth:`dump <Schema.dump>` method is the :meth:`load <Schema.load>` method, which deserializes an input dictionary to an application-level data structure.
+The reverse of the `dump <Schema.dump>` method is `load <Schema.load>`, which validates and deserializes 
+an input dictionary to an application-level data structure. 
 
-By default, :meth:`load <Schema.load>` will return a dictionary of field names mapped to the deserialized values.
+By default, :meth:`load <Schema.load>` will return a dictionary of field names mapped to deserialized values (or raise a :exc:`ValidationError <marshmallow.exceptions.ValidationError>` 
+with a dictionary of validation errors, which we'll :ref:`revisit later <validation>`).
 
 .. code-block:: python
 
@@ -160,7 +162,7 @@ Iterable collections of objects are also serializable and deserializable. Just s
 Validation
 ----------
 
-:meth:`Schema.load` (and its JSON-decoding counterpart, :meth:`Schema.loads`) raises a :exc:`ValidationError <marshmallow.exceptions.ValidationError>` error when invalid data are passed in. You can access the dictionary of validation errors from the `ValidationError.messages <marshmallow.exceptions.ValidationError.messages>` attribute. The data that was correctly serialized / deserialized is accessible in `ValidationError.valid_data <marshmallow.exceptions.ValidationError.valid_data>`. Some fields, such as the :class:`Email <fields.Email>` and :class:`URL <fields.URL>` fields, have built-in validation.
+:meth:`Schema.load` (and its JSON-decoding counterpart, :meth:`Schema.loads`) raises a :exc:`ValidationError <marshmallow.exceptions.ValidationError>` error when invalid data are passed in. You can access the dictionary of validation errors from the `ValidationError.messages <marshmallow.exceptions.ValidationError.messages>` attribute. The data that were correctly deserialized are accessible in `ValidationError.valid_data <marshmallow.exceptions.ValidationError.valid_data>`. Some fields, such as the :class:`Email <fields.Email>` and :class:`URL <fields.URL>` fields, have built-in validation.
 
 .. code-block:: python
 
@@ -169,8 +171,8 @@ Validation
     try:
         result = UserSchema().load({"name": "John", "email": "foo"})
     except ValidationError as err:
-        err.messages  # => {'email': ['"foo" is not a valid email address.']}
-        valid_data = err.valid_data  # => {'name': 'John'}
+        err.messages  # => {"email": ['"foo" is not a valid email address.']}
+        valid_data = err.valid_data  # => {"name": "John"}
 
 
 When validating a collection, the errors dictionary will be keyed on the indices of invalid items.
@@ -243,13 +245,11 @@ If validation fails, validation functions raise a :exc:`ValidationError <marshma
     except ValidationError as err:
         err.messages  # => {'quantity': ['Quantity must not be greater than 30.']}
 
-.. note::
-
-    If you have multiple validations to perform, you may also pass a collection (list, tuple, generator) of callables.
+You may also pass a collection (list, tuple, generator) of callables to ``validate``.
 
-.. note::
+.. warning::
 
-    :meth:`Schema.dump` also returns a dictionary of errors, which will include any ``ValidationErrors`` raised during serialization. However, ``required``, ``allow_none``, ``validate``, `@validates <marshmallow.decorators.validates>`, and `@validates_schema <marshmallow.decorators.validates_schema>` only apply during deserialization.
+    Validation only happens on deserialization, not serialization. To improve serialization performance, data passed to :meth:`Schema.dump` are considered valid.
 
 .. seealso::
 
diff --git a/docs/upgrading.rst b/docs/upgrading.rst
index b577d6ae..4e364f90 100644
--- a/docs/upgrading.rst
+++ b/docs/upgrading.rst
@@ -54,7 +54,7 @@ along with the valid data from the `ValidationError.valid_data
         errors = err.messages
         valid_data = err.valid_data
 
-:meth:`Schema().validate <marshmallow.Schema.validate>` always returns a dictionary of validation errors (same as 2.x with ``strict=False``).
+:meth:`Schema.validate() <marshmallow.Schema.validate>` always returns a dictionary of validation errors (same as 2.x with ``strict=False``).
 
 .. code-block:: python
 
@@ -113,6 +113,7 @@ and `validates_schema <marshmallow.decorators.validates_schema>` receive
             in_data["slug"] = in_data["slug"].lower().strip().replace(" ", "-")
             return in_data
 
+
 `Schema.handle_error <marshmallow.Schema.handle_error>` also receives ``many`` and ``partial`` as keyword arguments.
 
 .. code-block:: python
@@ -129,6 +130,41 @@ and `validates_schema <marshmallow.decorators.validates_schema>` receive
             raise AppError("An error occurred with input: {0}".format(data))
 
 
+Validation does not occur on serialization
+******************************************
+
+:meth:`Schema.dump() <marshmallow.Schema.dump>` will no longer validate and collect error messages. You *must* validate
+your data before serializing it.
+
+.. code-block:: python
+
+    from marshmallow import Schema, fields, ValidationError
+
+    invalid_data = dict(created_at="invalid")
+
+
+    class WidgetSchema(Schema):
+        created_at = fields.DateTime()
+
+
+    # 2.x
+    WidgetSchema(strict=True).dump(invalid_data)
+    # marshmallow.exceptions.ValidationError: {'created_at': ['"invalid" cannot be formatted as a datetime.']}
+
+    # 3.x
+    WidgetSchema().dump(invalid_data)
+    # AttributeError: 'str' object has no attribute 'isoformat'
+
+    # Instead, validate before dumping
+    schema = WidgetSchema()
+    try:
+        widget = schema.load(invalid_data)
+    except ValidationError:
+        print("handle errors...")
+    else:
+        dumped = schema.dump(widget)
+
+
 Deserializing invalid types raises a ``ValidationError``
 ********************************************************
 
diff --git a/src/marshmallow/fields.py b/src/marshmallow/fields.py
index c279af8b..10d029f0 100644
--- a/src/marshmallow/fields.py
+++ b/src/marshmallow/fields.py
@@ -276,7 +276,6 @@ class Field(FieldABC):
         :param str obj: The object to pull the key from.
         :param callable accessor: Function used to pull values from ``obj``.
         :param dict kwargs': Field-specific keyword arguments.
-        :raise ValidationError: In case of formatting problem
         """
         if self._CHECK_ATTRIBUTE:
             value = self.get_value(obj, attr, accessor=accessor)
@@ -339,7 +338,6 @@ class Field(FieldABC):
         :param str attr: The attribute or key on the object to be serialized.
         :param object obj: The object the value was pulled from.
         :param dict kwargs': Field-specific keyword arguments.
-        :raise ValidationError: In case of formatting or validation failure.
         :return: The serialized value
         """
         return value
@@ -493,12 +491,7 @@ class Nested(Field):
         schema = self.schema
         if nested_obj is None:
             return None
-        try:
-            return schema.dump(nested_obj, many=self.many)
-        except ValidationError as error:
-            raise ValidationError(
-                error.messages, valid_data=error.valid_data
-            ) from error
+        return schema.dump(nested_obj, many=self.many)
 
     def _test_collection(self, value):
         if self.many and not utils.is_collection(value):
@@ -794,15 +787,15 @@ class Number(Field):
 
     def _format_num(self, value):
         """Return the number value for value, given this field's `num_type`."""
-        # (value is True or value is False) is ~5x faster than isinstance(value, bool)
-        if value is True or value is False:
-            raise TypeError("value must be a Number, not a boolean.")
         return self.num_type(value)
 
     def _validated(self, value):
         """Format the value or raise a :exc:`ValidationError` if an error occurs."""
         if value is None:
             return None
+        # (value is True or value is False) is ~5x faster than isinstance(value, bool)
+        if value is True or value is False:
+            self.fail("invalid", input=value)
         try:
             return self._format_num(value)
         except (TypeError, ValueError) as error:
@@ -815,12 +808,10 @@ class Number(Field):
 
     def _serialize(self, value, attr, obj, **kwargs):
         """Return a string if `self.as_string=True`, otherwise return this field's `num_type`."""
-        ret = self._validated(value)
-        return (
-            self._to_string(ret)
-            if (self.as_string and ret not in (None, missing_))
-            else ret
-        )
+        if value is None:
+            return None
+        ret = self._format_num(value)
+        return self._to_string(ret) if self.as_string else ret
 
     def _deserialize(self, value, attr, data, **kwargs):
         return self._validated(value)
@@ -835,20 +826,19 @@ class Integer(Number):
     num_type = int
     default_error_messages = {"invalid": "Not a valid integer."}
 
-    # override Number
     def __init__(self, *, strict=False, **kwargs):
         self.strict = strict
         super().__init__(**kwargs)
 
     # override Number
-    def _format_num(self, value):
+    def _validated(self, value):
         if self.strict:
             if isinstance(value, numbers.Number) and isinstance(
                 value, numbers.Integral
             ):
-                return super()._format_num(value)
+                return super()._validated(value)
             self.fail("invalid", input=value)
-        return super()._format_num(value)
+        return super()._validated(value)
 
 
 class Float(Number):
@@ -870,8 +860,8 @@ class Float(Number):
         self.allow_nan = allow_nan
         super().__init__(as_string=as_string, **kwargs)
 
-    def _format_num(self, value):
-        num = super()._format_num(value)
+    def _validated(self, value):
+        num = super()._validated(value)
         if self.allow_nan is False:
             if math.isnan(num) or num == float("inf") or num == float("-inf"):
                 self.fail("special")
@@ -934,25 +924,22 @@ class Decimal(Number):
     # override Number
     def _format_num(self, value):
         num = decimal.Decimal(str(value))
-
         if self.allow_nan:
             if num.is_nan():
                 return decimal.Decimal("NaN")  # avoid sNaN, -sNaN and -NaN
-        else:
-            if num.is_nan() or num.is_infinite():
-                self.fail("special")
-
         if self.places is not None and num.is_finite():
             num = num.quantize(self.places, rounding=self.rounding)
-
         return num
 
     # override Number
     def _validated(self, value):
         try:
-            return super()._validated(value)
+            num = super()._validated(value)
         except decimal.InvalidOperation as error:
             raise self.make_error("invalid") from error
+        if not self.allow_nan and (num.is_nan() or num.is_infinite()):
+            raise self.make_error("special")
+        return num
 
     # override Number
     def _to_string(self, value):
@@ -1101,12 +1088,7 @@ class DateTime(Field):
         data_format = self.format or self.DEFAULT_FORMAT
         format_func = self.SERIALIZATION_FUNCS.get(data_format)
         if format_func:
-            try:
-                return format_func(value)
-            except (TypeError, AttributeError, ValueError) as error:
-                raise self.make_error(
-                    "format", input=value, obj_type=self.OBJ_TYPE
-                ) from error
+            return format_func(value)
         else:
             return value.strftime(data_format)
 
@@ -1208,10 +1190,7 @@ class Time(Field):
     def _serialize(self, value, attr, obj, **kwargs):
         if value is None:
             return None
-        try:
-            ret = value.isoformat()
-        except AttributeError as error:
-            raise self.make_error("format", input=value) from error
+        ret = value.isoformat()
         if value.microsecond:
             return ret[:15]
         return ret
@@ -1306,11 +1285,8 @@ class TimeDelta(Field):
     def _serialize(self, value, attr, obj, **kwargs):
         if value is None:
             return None
-        try:
-            base_unit = dt.timedelta(**{self.precision: 1})
-            return int(value.total_seconds() / base_unit.total_seconds())
-        except AttributeError as error:
-            raise self.make_error("format", input=value) from error
+        base_unit = dt.timedelta(**{self.precision: 1})
+        return int(value.total_seconds() / base_unit.total_seconds())
 
     def _deserialize(self, value, attr, data, **kwargs):
         try:
@@ -1387,8 +1363,6 @@ class Mapping(Field):
             return None
         if not self.value_field and not self.key_field:
             return value
-        if not isinstance(value, _Mapping):
-            self.fail("invalid")
 
         #  Serialize keys
         if self.key_field is None:
diff --git a/src/marshmallow/schema.py b/src/marshmallow/schema.py
index 12342c5a..58d888ba 100644
--- a/src/marshmallow/schema.py
+++ b/src/marshmallow/schema.py
@@ -450,51 +450,29 @@ class BaseSchema(base.SchemaABC):
         return value
 
     def _serialize(
-        self,
-        obj,
-        fields_dict,
-        *,
-        error_store,
-        many=False,
-        accessor=None,
-        dict_class=dict,
-        index_errors=True,
-        index=None
+        self, obj, fields_dict, *, many=False, accessor=None, dict_class=dict
     ):
         """Takes raw data (a dict, list, or other object) and a dict of
         fields to output and serializes the data based on those fields.
 
         :param obj: The actual object(s) from which the fields are taken from
         :param dict fields_dict: Mapping of field names to :class:`Field` objects.
-        :param ErrorStore error_store: Structure to store errors.
         :param bool many: Set to `True` if ``data`` should be serialized as
             a collection.
         :param callable accessor: Function to use for getting values from ``obj``.
         :param type dict_class: Dictionary class used to construct the output.
-        :param bool index_errors: Whether to store the index of invalid items in
-            ``self.errors`` when ``many=True``.
-        :param int index: Index of the item being serialized (for storing errors) if
-            serializing a collection, otherwise `None`.
         :return: A dictionary of the marshalled data
 
         .. versionchanged:: 1.0.0
             Renamed from ``marshal``.
         """
-        index = index if index_errors else None
         if many and obj is not None:
             self._pending = True
             ret = [
                 self._serialize(
-                    d,
-                    fields_dict,
-                    error_store=error_store,
-                    many=False,
-                    dict_class=dict_class,
-                    accessor=accessor,
-                    index=idx,
-                    index_errors=index_errors,
+                    d, fields_dict, many=False, dict_class=dict_class, accessor=accessor
                 )
-                for idx, d in enumerate(obj)
+                for d in obj
             ]
             self._pending = False
             return ret
@@ -502,17 +480,10 @@ class BaseSchema(base.SchemaABC):
         for attr_name, field_obj in fields_dict.items():
             if getattr(field_obj, "load_only", False):
                 continue
-            key = field_obj.data_key or attr_name
-            getter = lambda d: field_obj.serialize(attr_name, d, accessor=accessor)
-            value = self._call_and_store(
-                getter_func=getter,
-                data=obj,
-                field_name=key,
-                error_store=error_store,
-                index=index,
-            )
+            value = field_obj.serialize(attr_name, obj, accessor=accessor)
             if value is missing:
                 continue
+            key = field_obj.data_key or attr_name
             ret[key] = value
         return ret
 
@@ -532,47 +503,29 @@ class BaseSchema(base.SchemaABC):
             A :exc:`ValidationError <marshmallow.exceptions.ValidationError>` is raised
             if ``obj`` is invalid.
         """
-        error_store = ErrorStore()
-        errors = {}
         many = self.many if many is None else bool(many)
         if many and is_iterable_but_not_string(obj):
             obj = list(obj)
 
         if self._has_processors(PRE_DUMP):
-            try:
-                processed_obj = self._invoke_dump_processors(
-                    PRE_DUMP, obj, many=many, original_data=obj
-                )
-            except ValidationError as error:
-                errors = error.normalized_messages()
-                result = None
+            processed_obj = self._invoke_dump_processors(
+                PRE_DUMP, obj, many=many, original_data=obj
+            )
         else:
             processed_obj = obj
 
-        if not errors:
-            result = self._serialize(
-                processed_obj,
-                fields_dict=self.fields,
-                error_store=error_store,
-                many=many,
-                accessor=self.get_attribute,
-                dict_class=self.dict_class,
-                index_errors=self.opts.index_errors,
-            )
-            errors = error_store.errors
+        result = self._serialize(
+            processed_obj,
+            fields_dict=self.fields,
+            many=many,
+            accessor=self.get_attribute,
+            dict_class=self.dict_class,
+        )
 
-        if not errors and self._has_processors(POST_DUMP):
-            try:
-                result = self._invoke_dump_processors(
-                    POST_DUMP, result, many=many, original_data=obj
-                )
-            except ValidationError as error:
-                errors = error.normalized_messages()
-        if errors:
-            exc = ValidationError(errors, data=obj, valid_data=result)
-            # User-defined error handler
-            self.handle_error(exc, obj, many=many)
-            raise exc
+        if self._has_processors(POST_DUMP):
+            result = self._invoke_dump_processors(
+                POST_DUMP, result, many=many, original_data=obj
+            )
 
         return result
 


Test patch:
diff --git a/tests/test_decorators.py b/tests/test_decorators.py
index 44a543de..0d2ffc6b 100644
--- a/tests/test_decorators.py
+++ b/tests/test_decorators.py
@@ -724,32 +724,6 @@ def test_decorator_error_handling():  # noqa: C901
                 return item
             raise ValidationError("postloadmsg1", "foo")
 
-        @pre_dump()
-        def pre_dump_error1(self, item, **kwargs):
-            if item["foo"] != 2:
-                return item
-            errors = {"foo": ["predumpmsg1"], "bar": ["predumpmsg2", "predumpmsg3"]}
-            raise ValidationError(errors)
-
-        @pre_dump()
-        def pre_dump_error2(self, item, **kwargs):
-            if item["foo"] != 6:
-                return item
-            raise ValidationError("predumpmsg1", "foo")
-
-        @post_dump()
-        def post_dump_error1(self, item, **kwargs):
-            if item["foo"] != 3:
-                return item
-            errors = {"foo": ["postdumpmsg1"], "bar": ["postdumpmsg2", "postdumpmsg3"]}
-            raise ValidationError(errors)
-
-        @post_dump()
-        def post_dump_error2(self, item, **kwargs):
-            if item["foo"] != 7:
-                return item
-            raise ValidationError("postdumpmsg1", "foo")
-
     def make_item(foo, bar):
         data = schema.load({"foo": foo, "bar": bar})
         assert data is not None
@@ -776,26 +750,6 @@ def test_decorator_error_handling():  # noqa: C901
     assert len(errors["bar"]) == 2
     assert "postloadmsg2" in errors["bar"]
     assert "postloadmsg3" in errors["bar"]
-    with pytest.raises(ValidationError) as excinfo:
-        schema.dump(make_item(2, 1))
-    errors = excinfo.value.messages
-    assert "foo" in errors
-    assert len(errors["foo"]) == 1
-    assert errors["foo"][0] == "predumpmsg1"
-    assert "bar" in errors
-    assert len(errors["bar"]) == 2
-    assert "predumpmsg2" in errors["bar"]
-    assert "predumpmsg3" in errors["bar"]
-    with pytest.raises(ValidationError) as excinfo:
-        schema.dump(make_item(3, 1))
-    errors = excinfo.value.messages
-    assert "foo" in errors
-    assert len(errors["foo"]) == 1
-    assert errors["foo"][0] == "postdumpmsg1"
-    assert "bar" in errors
-    assert len(errors["bar"]) == 2
-    assert "postdumpmsg2" in errors["bar"]
-    assert "postdumpmsg3" in errors["bar"]
     with pytest.raises(ValidationError) as excinfo:
         schema.load({"foo": 4, "bar": 1})
     errors = excinfo.value.messages
@@ -810,18 +764,6 @@ def test_decorator_error_handling():  # noqa: C901
     assert "foo" in errors
     assert len(errors["foo"]) == 1
     assert errors["foo"][0] == "postloadmsg1"
-    with pytest.raises(ValidationError) as excinfo:
-        schema.dump(make_item(6, 1))
-    errors = excinfo.value.messages
-    assert "foo" in errors
-    assert len(errors["foo"]) == 1
-    assert errors["foo"][0] == "predumpmsg1"
-    with pytest.raises(ValidationError) as excinfo:
-        schema.dump(make_item(7, 1))
-    errors = excinfo.value.messages
-    assert "foo" in errors
-    assert len(errors["foo"]) == 1
-    assert errors["foo"][0] == "postdumpmsg1"
     with pytest.raises(ValidationError) as excinfo:
         schema.load({"foo": 8, "bar": 1})
     errors = excinfo.value.messages
@@ -873,20 +815,6 @@ def test_decorator_error_handling_with_dump(decorator):
     schema.load({})
 
 
-@pytest.mark.parametrize("decorator", [pre_dump, post_dump])
-def test_decorator_error_handling_with_dump_dict_error(decorator):
-    class ExampleSchema(Schema):
-        @decorator
-        def raise_value_error(self, item, **kwargs):
-            raise ValidationError({"foo": "error"}, "nested")
-
-    schema = ExampleSchema()
-    with pytest.raises(ValidationError) as exc:
-        schema.dump(object())
-    assert exc.value.messages == {"nested": {"foo": "error"}}
-    schema.load({})
-
-
 class Nested:
     def __init__(self, foo):
         self.foo = foo
diff --git a/tests/test_deserialization.py b/tests/test_deserialization.py
index 3f03231f..7536cf75 100644
--- a/tests/test_deserialization.py
+++ b/tests/test_deserialization.py
@@ -45,7 +45,7 @@ class TestFieldDeserialization:
         assert math.isclose(field.deserialize("12.3"), 12.3)
         assert math.isclose(field.deserialize(12.3), 12.3)
 
-    @pytest.mark.parametrize("in_val", ["bad", "", {}])
+    @pytest.mark.parametrize("in_val", ["bad", "", {}, True, False])
     def test_invalid_float_field_deserialization(self, in_val):
         field = fields.Float()
         with pytest.raises(ValidationError) as excinfo:
diff --git a/tests/test_fields.py b/tests/test_fields.py
index 6fda5e5b..5364b83c 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -92,15 +92,6 @@ class TestField:
         result = MySchema().dump({"name": "Monty", "foo": 42})
         assert result == {"_NaMe": "Monty"}
 
-    def test_number_fields_prohbits_boolean(self):
-        strict_field = fields.Float()
-        with pytest.raises(ValidationError) as excinfo:
-            strict_field.serialize("value", {"value": False})
-        assert excinfo.value.args[0] == "Not a valid number."
-        with pytest.raises(ValidationError) as excinfo:
-            strict_field.serialize("value", {"value": True})
-        assert excinfo.value.args[0] == "Not a valid number."
-
 
 class TestParentAndName:
     class MySchema(Schema):
diff --git a/tests/test_schema.py b/tests/test_schema.py
index a7f2c305..dde4d8ef 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -58,48 +58,6 @@ def test_serializer_dump(user):
     s = UserSchema()
     result = s.dump(user)
     assert result["name"] == user.name
-    bad_user = User(name="Monty", age="badage")
-    with pytest.raises(ValidationError):
-        s.dump(bad_user)
-
-
-def test_dump_raises_with_dict_of_errors():
-    s = UserSchema()
-    bad_user = User(name="Monty", age="badage")
-    with pytest.raises(ValidationError) as excinfo:
-        s.dump(bad_user)
-    errors = excinfo.value.messages
-    assert "age" in errors
-
-
-@pytest.mark.parametrize("SchemaClass", [UserSchema, UserMetaSchema])
-def test_dump_mode_raises_error(SchemaClass):
-    s = SchemaClass()
-    bad_user = User(name="Monty", homepage="http://www.foo.bar", balance="dummy")
-    with pytest.raises(ValidationError) as excinfo:
-        s.dump(bad_user)
-    exc = excinfo.value
-    assert list(exc.messages.keys()) == ["balance"]
-
-    assert type(exc.messages) == dict
-    assert exc.messages == {"balance": ["Not a valid number."]}
-
-
-def test_dump_resets_errors():
-    class MySchema(Schema):
-        age = fields.Int()
-
-    schema = MySchema()
-    with pytest.raises(ValidationError) as excinfo:
-        schema.dump(User("Joe", age="dummy"))
-    errors = excinfo.value.messages
-    assert len(errors["age"]) == 1
-    assert "Not a valid integer." in errors["age"][0]
-    with pytest.raises(ValidationError) as excinfo:
-        schema.dump(User("Steve", age="__dummy"))
-    errors = excinfo.value.messages
-    assert len(errors["age"]) == 1
-    assert "Not a valid integer." in errors["age"][0]
 
 
 def test_load_resets_errors():
@@ -143,47 +101,6 @@ def test_load_validation_error_stores_input_data_and_valid_data():
         pytest.fail("Data is invalid. Expected a ValidationError to be raised.")
 
 
-def test_dump_validation_error_stores_partially_valid_data():
-    class FailOnDump(fields.Field):
-        def _serialize(self, *args, **kwargs):
-            raise ValidationError("fail")
-
-    class MySchema(Schema):
-        always_valid = fields.DateTime()
-        always_invalid = FailOnDump()
-
-    schema = MySchema()
-    input_data = {"always_valid": dt.datetime.utcnow(), "always_invalid": 24}
-    try:
-        schema.dump(input_data)
-    except ValidationError as err:
-        # err.data is the raw input data
-        assert err.data == input_data
-        assert "always_valid" in err.valid_data
-        # err.valid_data contains valid, serialized data
-        assert isinstance(err.valid_data["always_valid"], str)
-        # excludes invalid data
-        assert "always_invalid" not in err.valid_data
-    else:
-        pytest.fail("Data is invalid. Expected a ValidationError to be raised.")
-
-
-def test_dump_resets_error_fields():
-    class MySchema(Schema):
-        age = fields.Int()
-
-    schema = MySchema()
-    with pytest.raises(ValidationError) as excinfo:
-        schema.dump(User("Joe", age="dummy"))
-    exc = excinfo.value
-    assert len(exc.messages.keys()) == 1
-
-    with pytest.raises(ValidationError) as excinfo:
-        schema.dump(User("Joe", age="__dummy"))
-    exc = excinfo.value
-    assert len(exc.messages.keys()) == 1
-
-
 def test_load_resets_error_fields():
     class MySchema(Schema):
         email = fields.Email()
@@ -217,13 +134,6 @@ def test_errored_fields_do_not_appear_in_output():
     assert "foo" in errors
     assert "foo" not in data
 
-    with pytest.raises(ValidationError) as excinfo:
-        sch.dump({"foo": 2})
-    data, errors = excinfo.value.valid_data, excinfo.value.messages
-
-    assert "foo" in errors
-    assert "foo" not in data
-
 
 def test_load_many_stores_error_indices():
     s = UserSchema()
@@ -264,36 +174,6 @@ def test_multiple_errors_can_be_stored_for_a_given_index():
     assert "bar" in errors[1]
 
 
-def test_dump_many_stores_error_indices():
-    s = UserSchema()
-    u1, u2 = User("Mick", balance=42), User("Keith", balance="dummy")
-
-    with pytest.raises(ValidationError) as excinfo:
-        s.dump([u1, u2], many=True)
-    errors = excinfo.value.messages
-    assert 1 in errors
-    assert len(errors[1]) == 1
-
-    assert "balance" in errors[1]
-
-
-def test_dump_many_doesnt_stores_error_indices_when_index_errors_is_false():
-    class NoIndex(Schema):
-        age = fields.Int()
-
-        class Meta:
-            index_errors = False
-
-    s = NoIndex()
-    u1, u2 = User("Mick", age=42), User("Keith", age="dummy")
-
-    with pytest.raises(ValidationError) as excinfo:
-        s.dump([u1, u2], many=True)
-    errors = excinfo.value.messages
-    assert 1 not in errors
-    assert "age" in errors
-
-
 def test_dump_returns_a_dict(user):
     s = UserSchema()
     result = s.dump(user)
@@ -684,15 +564,12 @@ def test_serializing_dict():
     user = {
         "name": "foo",
         "email": "foo@bar.com",
-        "age": "badage",
+        "age": 42,
         "various_data": {"foo": "bar"},
     }
-    with pytest.raises(ValidationError) as excinfo:
-        UserSchema().dump(user)
-    data, errors = excinfo.value.valid_data, excinfo.value.messages
+    data = UserSchema().dump(user)
     assert data["name"] == "foo"
-    assert "age" in errors
-    assert "age" not in data
+    assert data["age"] == 42
     assert data["various_data"] == {"foo": "bar"}
 
 
@@ -738,22 +615,6 @@ def test_can_serialize_time(user, serialized_user):
     assert serialized_user["time_registered"] == expected
 
 
-def test_invalid_time():
-    u = User("Joe", time_registered="foo")
-    with pytest.raises(ValidationError) as excinfo:
-        UserSchema().dump(u)
-    errors = excinfo.value.messages
-    assert '"foo" cannot be formatted as a time.' in errors["time_registered"]
-
-
-def test_invalid_date():
-    u = User("Joe", birthdate="foo")
-    with pytest.raises(ValidationError) as excinfo:
-        UserSchema().dump(u)
-    errors = excinfo.value.messages
-    assert '"foo" cannot be formatted as a date.' in errors["birthdate"]
-
-
 def test_invalid_dict_but_okay():
     u = User("Joe", various_data="baddict")
     UserSchema().dump(u)
@@ -1792,13 +1653,6 @@ class MySchema(Schema):
     def handle_error(self, errors, obj, **kwargs):
         raise CustomError("Something bad happened")
 
-
-class TestHandleError:
-    def test_dump_with_custom_error_handler(self, user):
-        user.age = "notavalidage"
-        with pytest.raises(CustomError):
-            MySchema().dump(user)
-
     def test_load_with_custom_error_handler(self):
         in_data = {"email": "invalid"}
 
@@ -2083,22 +1937,6 @@ class TestNestedSchema:
         # No problems with collaborators
         assert "collaborators" not in errors
 
-    def test_nested_dump_errors(self, blog):
-        blog.user.age = "foo"
-        with pytest.raises(ValidationError) as excinfo:
-            BlogSchema().dump(blog)
-        errors = excinfo.value.messages
-        assert "age" in errors["user"]
-        assert len(errors["user"]["age"]) == 1
-        assert "Not a valid number." in errors["user"]["age"][0]
-        # No problems with collaborators
-        assert "collaborators" not in errors
-
-    def test_nested_dump(self, blog):
-        blog.user.age = "foo"
-        with pytest.raises(ValidationError, match="age"):
-            BlogSchema().dump(blog)
-
     def test_nested_method_field(self, blog):
         data = BlogSchema().dump(blog)
         assert data["user"]["is_old"]
@@ -2110,13 +1948,6 @@ class TestNestedSchema:
         expected = blog.collaborators[0].name.lower()
         assert data["collaborators"][0]["lowername"] == expected
 
-    def test_invalid_float_field(self):
-        user = User("Joe", age="1b2")
-        with pytest.raises(ValidationError) as excinfo:
-            UserSchema().dump(user)
-        errors = excinfo.value.messages
-        assert "age" in errors
-
     def test_serializer_meta_with_nested_fields(self, blog, user):
         data = BlogSchemaMeta().dump(blog)
         assert data["title"] == blog.title
@@ -2184,35 +2015,6 @@ class TestNestedSchema:
         assert "inner" in errors
         assert "_schema" in errors["inner"]
 
-    def test_dump_validation_error(self):
-        class Child:
-            def __init__(self, foo, bar):
-                self.foo = foo
-                self.bar = bar
-
-        class Parent:
-            def __init__(self, foo, bar):
-                self.foo = foo
-                self.bar = bar
-
-        class ChildSchema(Schema):
-            foo = fields.Int()
-            bar = fields.Int()
-
-        class ParentSchema(Schema):
-            foo = fields.Nested(ChildSchema)
-            bar = fields.Int()
-
-        child = Child(foo="dummy", bar=42)
-        parent = Parent(foo=child, bar=42)
-
-        with pytest.raises(ValidationError) as excinfo:
-            ParentSchema().dump(parent)
-        errors = excinfo.value.messages
-        data = excinfo.value.valid_data
-        assert data == {"foo": {"bar": 42}, "bar": 42}
-        assert errors == {"foo": {"foo": ["Not a valid integer."]}}
-
     @pytest.mark.parametrize("unknown", (None, RAISE, INCLUDE, EXCLUDE))
     def test_nested_unknown_validation(self, unknown):
         class ChildSchema(Schema):
diff --git a/tests/test_serialization.py b/tests/test_serialization.py
index db7b141f..a1953b84 100644
--- a/tests/test_serialization.py
+++ b/tests/test_serialization.py
@@ -4,12 +4,10 @@ import datetime as dt
 import itertools
 import decimal
 import uuid
-import math
 
 import pytest
 
 from marshmallow import Schema, fields, missing as missing_
-from marshmallow.exceptions import ValidationError
 
 from tests.base import User, ALL_FIELDS, central
 
@@ -121,9 +119,6 @@ class TestFieldSerialization:
         user.uuid1 = "{12345678-1234-5678-1234-567812345678}"
         user.uuid2 = uuid.UUID("12345678123456781234567812345678")
         user.uuid3 = None
-        user.uuid4 = "this is not a UUID"
-        user.uuid5 = 42
-        user.uuid6 = {}
 
         field = fields.UUID()
         assert isinstance(field.serialize("uuid1", user), str)
@@ -131,20 +126,12 @@ class TestFieldSerialization:
         assert isinstance(field.serialize("uuid2", user), str)
         assert field.serialize("uuid2", user) == "12345678-1234-5678-1234-567812345678"
         assert field.serialize("uuid3", user) is None
-        with pytest.raises(ValidationError):
-            field.serialize("uuid4", user)
-        with pytest.raises(ValidationError):
-            field.serialize("uuid5", user)
-        with pytest.raises(ValidationError):
-            field.serialize("uuid6", user)
 
     def test_decimal_field(self, user):
         user.m1 = 12
         user.m2 = "12.355"
         user.m3 = decimal.Decimal(1)
         user.m4 = None
-        user.m5 = "abc"
-        user.m6 = [1, 2]
 
         field = fields.Decimal()
         assert isinstance(field.serialize("m1", user), decimal.Decimal)
@@ -154,10 +141,6 @@ class TestFieldSerialization:
         assert isinstance(field.serialize("m3", user), decimal.Decimal)
         assert field.serialize("m3", user) == decimal.Decimal(1)
         assert field.serialize("m4", user) is None
-        with pytest.raises(ValidationError):
-            field.serialize("m5", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m6", user)
 
         field = fields.Decimal(1)
         assert isinstance(field.serialize("m1", user), decimal.Decimal)
@@ -167,10 +150,6 @@ class TestFieldSerialization:
         assert isinstance(field.serialize("m3", user), decimal.Decimal)
         assert field.serialize("m3", user) == decimal.Decimal(1)
         assert field.serialize("m4", user) is None
-        with pytest.raises(ValidationError):
-            field.serialize("m5", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m6", user)
 
         field = fields.Decimal(1, decimal.ROUND_DOWN)
         assert isinstance(field.serialize("m1", user), decimal.Decimal)
@@ -180,18 +159,12 @@ class TestFieldSerialization:
         assert isinstance(field.serialize("m3", user), decimal.Decimal)
         assert field.serialize("m3", user) == decimal.Decimal(1)
         assert field.serialize("m4", user) is None
-        with pytest.raises(ValidationError):
-            field.serialize("m5", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m6", user)
 
     def test_decimal_field_string(self, user):
         user.m1 = 12
         user.m2 = "12.355"
         user.m3 = decimal.Decimal(1)
         user.m4 = None
-        user.m5 = "abc"
-        user.m6 = [1, 2]
 
         field = fields.Decimal(as_string=True)
         assert isinstance(field.serialize("m1", user), str)
@@ -201,10 +174,6 @@ class TestFieldSerialization:
         assert isinstance(field.serialize("m3", user), str)
         assert field.serialize("m3", user) == "1"
         assert field.serialize("m4", user) is None
-        with pytest.raises(ValidationError):
-            field.serialize("m5", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m6", user)
 
         field = fields.Decimal(1, as_string=True)
         assert isinstance(field.serialize("m1", user), str)
@@ -214,10 +183,6 @@ class TestFieldSerialization:
         assert isinstance(field.serialize("m3", user), str)
         assert field.serialize("m3", user) == "1.0"
         assert field.serialize("m4", user) is None
-        with pytest.raises(ValidationError):
-            field.serialize("m5", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m6", user)
 
         field = fields.Decimal(1, decimal.ROUND_DOWN, as_string=True)
         assert isinstance(field.serialize("m1", user), str)
@@ -227,10 +192,6 @@ class TestFieldSerialization:
         assert isinstance(field.serialize("m3", user), str)
         assert field.serialize("m3", user) == "1.0"
         assert field.serialize("m4", user) is None
-        with pytest.raises(ValidationError):
-            field.serialize("m5", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m6", user)
 
     def test_decimal_field_special_values(self, user):
         user.m1 = "-NaN"
@@ -286,59 +247,14 @@ class TestFieldSerialization:
         assert m6s == user.m6
 
     def test_decimal_field_special_values_not_permitted(self, user):
-        user.m1 = "-NaN"
-        user.m2 = "NaN"
-        user.m3 = "-sNaN"
-        user.m4 = "sNaN"
-        user.m5 = "-Infinity"
-        user.m6 = "Infinity"
         user.m7 = "-0"
 
         field = fields.Decimal(places=2)
 
-        with pytest.raises(ValidationError):
-            field.serialize("m1", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m2", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m3", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m4", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m5", user)
-        with pytest.raises(ValidationError):
-            field.serialize("m6", user)
-
         m7s = field.serialize("m7", user)
         assert isinstance(m7s, decimal.Decimal)
         assert m7s.is_zero() and m7s.is_signed()
 
-    @pytest.mark.parametrize("allow_nan", (None, False, True))
-    @pytest.mark.parametrize("value", ("nan", "-nan", "inf", "-inf"))
-    def test_float_field_allow_nan(self, value, allow_nan, user):
-
-        user.key = value
-
-        if allow_nan is None:
-            # Test default case is False
-            field = fields.Float()
-        else:
-            field = fields.Float(allow_nan=allow_nan)
-
-        if allow_nan is True:
-            res = field.serialize("key", user)
-            assert isinstance(res, float)
-            if value.endswith("nan"):
-                assert math.isnan(res)
-            else:
-                assert res == float(value)
-        else:
-            with pytest.raises(ValidationError) as excinfo:
-                field.serialize("key", user)
-            assert str(excinfo.value.args[0]) == (
-                "Special numeric values (nan or infinity) are not permitted."
-            )
-
     def test_decimal_field_fixed_point_representation(self, user):
         """
         Test we get fixed-point string representation for a Decimal number that would normally
@@ -419,12 +335,6 @@ class TestFieldSerialization:
         field = fields.Dict(keys=fields.Str, values=fields.Decimal)
         assert field.serialize("various_data", user) == {"1": 1}
 
-    def test_structured_dict_validates(self, user):
-        user.various_data = {"foo": "bar"}
-        field = fields.Dict(values=fields.Decimal)
-        with pytest.raises(ValidationError):
-            field.serialize("various_data", user)
-
     def test_url_field_serialize_none(self, user):
         user.homepage = None
         field = fields.Url()
@@ -498,17 +408,6 @@ class TestFieldSerialization:
             "Years": 999,
         }
 
-    @pytest.mark.parametrize("value", ["invalid", [], 24])
-    def test_datetime_invalid_serialization(self, value, user):
-        field = fields.DateTime()
-        user.created = value
-
-        with pytest.raises(ValidationError) as excinfo:
-            field.serialize("created", user)
-        assert excinfo.value.args[
-            0
-        ] == '"{}" cannot be formatted as a datetime.'.format(value)
-
     @pytest.mark.parametrize("fmt", ["rfc", "rfc822"])
     @pytest.mark.parametrize(
         ("value", "expected"),
@@ -580,15 +479,6 @@ class TestFieldSerialization:
         user.time_registered = None
         assert field.serialize("time_registered", user) is None
 
-    @pytest.mark.parametrize("in_data", ["badvalue", "", [], 42])
-    def test_invalid_time_field_serialization(self, in_data, user):
-        field = fields.Time()
-        user.time_registered = in_data
-        with pytest.raises(ValidationError) as excinfo:
-            field.serialize("time_registered", user)
-        msg = '"{}" cannot be formatted as a time.'.format(in_data)
-        assert excinfo.value.args[0] == msg
-
     def test_date_field(self, user):
         field = fields.Date()
         assert field.serialize("birthdate", user) == user.birthdate.isoformat()
@@ -596,15 +486,6 @@ class TestFieldSerialization:
         user.birthdate = None
         assert field.serialize("birthdate", user) is None
 
-    @pytest.mark.parametrize("in_data", ["badvalue", "", [], 42])
-    def test_invalid_date_field_serialization(self, in_data, user):
-        field = fields.Date()
-        user.birthdate = in_data
-        with pytest.raises(ValidationError) as excinfo:
-            field.serialize("birthdate", user)
-        msg = '"{}" cannot be formatted as a date.'.format(in_data)
-        assert excinfo.value.args[0] == msg
-
     def test_timedelta_field(self, user):
         user.d1 = dt.timedelta(days=1, seconds=1, microseconds=1)
         user.d2 = dt.timedelta(days=0, seconds=86401, microseconds=1)
@@ -689,12 +570,6 @@ class TestFieldSerialization:
         result = field.serialize("dtimes", obj)
         assert all([type(each) == str for each in result])
 
-    def test_list_field_with_error(self):
-        obj = DateTimeList(["invaliddate"])
-        field = fields.List(fields.DateTime)
-        with pytest.raises(ValidationError):
-            field.serialize("dtimes", obj)
-
     def test_list_field_serialize_none_returns_none(self):
         obj = DateTimeList(None)
         field = fields.List(fields.DateTime)
@@ -719,16 +594,6 @@ class TestFieldSerialization:
         result = field.serialize("dtimes", obj)
         assert len(result) == 2
 
-    def test_list_field_work_with_generators_error(self):
-        def custom_generator():
-            for dtime in [dt.datetime.utcnow(), "m", dt.datetime.now()]:
-                yield dtime
-
-        obj = DateTimeList(custom_generator())
-        field = fields.List(fields.DateTime)
-        with pytest.raises(ValidationError):
-            field.serialize("dtimes", obj)
-
     def test_list_field_work_with_generators_empty_generator_returns_none_for_every_non_returning_yield_statement(  # noqa: B950
         self
     ):
@@ -790,19 +655,6 @@ class TestFieldSerialization:
         assert type(result[0]) == str
         assert type(result[1]) == int
 
-    @pytest.mark.parametrize(
-        "obj",
-        [
-            DateTimeIntegerTuple(("invaliddate", 42)),
-            DateTimeIntegerTuple((dt.datetime.utcnow(), "invalidint")),
-            DateTimeIntegerTuple(("invaliddate", "invalidint")),
-        ],
-    )
-    def test_tuple_field_with_error(self, obj):
-        field = fields.Tuple([fields.DateTime, fields.Integer])
-        with pytest.raises(ValidationError):
-            field.serialize("dtime_int", obj)
-
     def test_tuple_field_serialize_none_returns_none(self):
         obj = DateTimeIntegerTuple(None)
         field = fields.Tuple([fields.DateTime, fields.Integer])