﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
37459	Parallel test runner crashes with MaybeEncodingError when a subTest fails in a test case with unpicklable attributes	Oscar van Leusen		"= Summary =

With `--parallel`, a failing `subTest` in a test case that has any unpicklable instance attribute (for example, a `MagicMock` or a test `Client` assigned in `setUp`) aborts the whole run with `multiprocessing.pool.MaybeEncodingError: Error sending result`, instead of reporting the failure.

`RemoteTestResult.check_subtest_picklable()` runs while the test is executing. At that point `SimpleTestCase.__getstate__` filters out unpicklable attributes, because `self._outcome` is set. The events are pickled again when the worker sends its results, after the test has finished. By then unittest has reset `_outcome` to `None`, so `__getstate__` returns the full `__dict__` and pickling fails. The check passes on different state from what is actually sent.

Storing mocks or extra clients on `self` in `setUp` is common, so any subtest failure in such a test crashes the parallel run. The built-in `self.client` is unaffected only because `_pre_setup()` assigns it on the class.

= Minimal Reproduction =

Reproduced on 6.1.2 and 5.2.18, Python 3.12, with both the spawn and fork start methods. Requires tblib (`pip install django tblib`)

settings.py:
{{{
SECRET_KEY = ""x""
DATABASES = {""default"": {""ENGINE"": ""django.db.backends.sqlite3"", ""NAME"": "":memory:""}}
}}}

tests.py:

{{{
from unittest.mock import MagicMock

from django.test import SimpleTestCase


class SubTestWithMock(SimpleTestCase):
    def setUp(self):
        self.mock = MagicMock()

    def test_failing_subtest(self):
        with self.subTest(i=1):
            self.fail(""expected failure"")


class Other(SimpleTestCase):  # A second class so the run actually uses workers
    def test_ok(self):
        pass
}}}

**Run with**: `DJANGO_SETTINGS_MODULE=settings PYTHONPATH=. python -m django test tests --parallel 2`

**Expected**: `FAILED (failures=1)`.

**Actual**: the run aborts with
{{{
multiprocessing.pool.MaybeEncodingError: Error sending result: ...
Reason: 'PicklingError(""Can't pickle <class 'unittest.mock.MagicMock'>: it's not the same object as unittest.mock.MagicMock"")'
}}}

= Workaround =

With `--parallel 1`, or without including an un-pickleable attribute, the failure is reported normally."	Uncategorized	new	Testing framework	6.1	Normal			Oscar van Leusen	Unreviewed	0	0	0	0	0	0
