diff --git a/django/test/runner.py b/django/test/runner.py index 4996b82f7e..e0169e6b5f 100644 --- a/django/test/runner.py +++ b/django/test/runner.py @@ -56,6 +56,13 @@ class DebugSQLTextTestResult(unittest.TextTestResult): self.debug_sql_stream.seek(0) self.failures[-1] = self.failures[-1] + (self.debug_sql_stream.read(),) + def addSubTest(self, test, subtest, err): + super().addSubTest(test, subtest, err) + if err is not None: + self.debug_sql_stream.seek(0) + errors = self.failures if issubclass(err[0], test.failureException) else self.errors + errors[-1] = errors[-1] + (self.debug_sql_stream.read(),) + def printErrorList(self, flavour, errors): for test, err, sql_debug in errors: self.stream.writeln(self.separator1) diff --git a/tests/test_runner/test_debug_sql.py b/tests/test_runner/test_debug_sql.py index 1b36fbc876..2ac7203051 100644 --- a/tests/test_runner/test_debug_sql.py +++ b/tests/test_runner/test_debug_sql.py @@ -26,12 +26,32 @@ class TestDebugSQL(unittest.TestCase): Person.objects.filter(first_name='error').count() raise Exception + class PassingSubTest(TestCase): + def runTest(self): + with self.subTest(): + Person.objects.filter(first_name='subtest-pass').count() + + class FailingSubTest(TestCase): + def runTest(self): + with self.subTest(): + Person.objects.filter(first_name='subtest-fail').count() + self.fail() + + class ErrorSubTest(TestCase): + def runTest(self): + with self.subTest(): + Person.objects.filter(first_name='subtest-error').count() + raise Exception + def _test_output(self, verbosity): runner = DiscoverRunner(debug_sql=True, verbosity=0) suite = runner.test_suite() suite.addTest(self.FailingTest()) suite.addTest(self.ErrorTest()) suite.addTest(self.PassingTest()) + suite.addTest(self.PassingSubTest()) + suite.addTest(self.FailingSubTest()) + suite.addTest(self.ErrorSubTest()) old_config = runner.setup_databases() stream = StringIO() resultclass = runner.get_resultclass() @@ -65,6 +85,12 @@ class TestDebugSQL(unittest.TestCase): ('''SELECT COUNT(*) AS "__count" ''' '''FROM "test_runner_person" WHERE ''' '''"test_runner_person"."first_name" = 'fail';'''), + ('''SELECT COUNT(*) AS "__count" ''' + '''FROM "test_runner_person" WHERE ''' + '''"test_runner_person"."first_name" = 'subtest-error';'''), + ('''SELECT COUNT(*) AS "__count" ''' + '''FROM "test_runner_person" WHERE ''' + '''"test_runner_person"."first_name" = 'subtest-fail';'''), ] verbose_expected_outputs = [ @@ -73,9 +99,17 @@ class TestDebugSQL(unittest.TestCase): 'runTest (test_runner.test_debug_sql.{}FailingTest) ... FAIL', 'runTest (test_runner.test_debug_sql.{}ErrorTest) ... ERROR', 'runTest (test_runner.test_debug_sql.{}PassingTest) ... ok', + 'runTest (test_runner.test_debug_sql.{}PassingSubTest) ... ok', + # If there are errors/failures in subtests but not in test itself, + # the status is not written. That behavior comes from Python. + 'runTest (test_runner.test_debug_sql.{}FailingSubTest) ...', + 'runTest (test_runner.test_debug_sql.{}ErrorSubTest) ...', ] ] + [ ('''SELECT COUNT(*) AS "__count" ''' '''FROM "test_runner_person" WHERE ''' '''"test_runner_person"."first_name" = 'pass';'''), + ('''SELECT COUNT(*) AS "__count" ''' + '''FROM "test_runner_person" WHERE ''' + '''"test_runner_person"."first_name" = 'subtest-pass';'''), ]