tests.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. """
  2. Tests for django test runner
  3. """
  4. import unittest
  5. from unittest import mock
  6. from admin_scripts.tests import AdminScriptTestCase
  7. from django import db
  8. from django.conf import settings
  9. from django.core.exceptions import ImproperlyConfigured
  10. from django.core.management import call_command
  11. from django.core.management.base import SystemCheckError
  12. from django.test import (
  13. SimpleTestCase, TransactionTestCase, skipUnlessDBFeature,
  14. )
  15. from django.test.runner import DiscoverRunner
  16. from django.test.testcases import connections_support_transactions
  17. from django.test.utils import (
  18. captured_stderr, dependency_ordered, get_unique_databases_and_mirrors,
  19. )
  20. from django.utils.deprecation import RemovedInDjango50Warning
  21. from .models import B, Person, Through
  22. class DependencyOrderingTests(unittest.TestCase):
  23. def test_simple_dependencies(self):
  24. raw = [
  25. ('s1', ('s1_db', ['alpha'])),
  26. ('s2', ('s2_db', ['bravo'])),
  27. ('s3', ('s3_db', ['charlie'])),
  28. ]
  29. dependencies = {
  30. 'alpha': ['charlie'],
  31. 'bravo': ['charlie'],
  32. }
  33. ordered = dependency_ordered(raw, dependencies=dependencies)
  34. ordered_sigs = [sig for sig, value in ordered]
  35. self.assertIn('s1', ordered_sigs)
  36. self.assertIn('s2', ordered_sigs)
  37. self.assertIn('s3', ordered_sigs)
  38. self.assertLess(ordered_sigs.index('s3'), ordered_sigs.index('s1'))
  39. self.assertLess(ordered_sigs.index('s3'), ordered_sigs.index('s2'))
  40. def test_chained_dependencies(self):
  41. raw = [
  42. ('s1', ('s1_db', ['alpha'])),
  43. ('s2', ('s2_db', ['bravo'])),
  44. ('s3', ('s3_db', ['charlie'])),
  45. ]
  46. dependencies = {
  47. 'alpha': ['bravo'],
  48. 'bravo': ['charlie'],
  49. }
  50. ordered = dependency_ordered(raw, dependencies=dependencies)
  51. ordered_sigs = [sig for sig, value in ordered]
  52. self.assertIn('s1', ordered_sigs)
  53. self.assertIn('s2', ordered_sigs)
  54. self.assertIn('s3', ordered_sigs)
  55. # Explicit dependencies
  56. self.assertLess(ordered_sigs.index('s2'), ordered_sigs.index('s1'))
  57. self.assertLess(ordered_sigs.index('s3'), ordered_sigs.index('s2'))
  58. # Implied dependencies
  59. self.assertLess(ordered_sigs.index('s3'), ordered_sigs.index('s1'))
  60. def test_multiple_dependencies(self):
  61. raw = [
  62. ('s1', ('s1_db', ['alpha'])),
  63. ('s2', ('s2_db', ['bravo'])),
  64. ('s3', ('s3_db', ['charlie'])),
  65. ('s4', ('s4_db', ['delta'])),
  66. ]
  67. dependencies = {
  68. 'alpha': ['bravo', 'delta'],
  69. 'bravo': ['charlie'],
  70. 'delta': ['charlie'],
  71. }
  72. ordered = dependency_ordered(raw, dependencies=dependencies)
  73. ordered_sigs = [sig for sig, aliases in ordered]
  74. self.assertIn('s1', ordered_sigs)
  75. self.assertIn('s2', ordered_sigs)
  76. self.assertIn('s3', ordered_sigs)
  77. self.assertIn('s4', ordered_sigs)
  78. # Explicit dependencies
  79. self.assertLess(ordered_sigs.index('s2'), ordered_sigs.index('s1'))
  80. self.assertLess(ordered_sigs.index('s4'), ordered_sigs.index('s1'))
  81. self.assertLess(ordered_sigs.index('s3'), ordered_sigs.index('s2'))
  82. self.assertLess(ordered_sigs.index('s3'), ordered_sigs.index('s4'))
  83. # Implicit dependencies
  84. self.assertLess(ordered_sigs.index('s3'), ordered_sigs.index('s1'))
  85. def test_circular_dependencies(self):
  86. raw = [
  87. ('s1', ('s1_db', ['alpha'])),
  88. ('s2', ('s2_db', ['bravo'])),
  89. ]
  90. dependencies = {
  91. 'bravo': ['alpha'],
  92. 'alpha': ['bravo'],
  93. }
  94. with self.assertRaises(ImproperlyConfigured):
  95. dependency_ordered(raw, dependencies=dependencies)
  96. def test_own_alias_dependency(self):
  97. raw = [
  98. ('s1', ('s1_db', ['alpha', 'bravo']))
  99. ]
  100. dependencies = {
  101. 'alpha': ['bravo']
  102. }
  103. with self.assertRaises(ImproperlyConfigured):
  104. dependency_ordered(raw, dependencies=dependencies)
  105. # reordering aliases shouldn't matter
  106. raw = [
  107. ('s1', ('s1_db', ['bravo', 'alpha']))
  108. ]
  109. with self.assertRaises(ImproperlyConfigured):
  110. dependency_ordered(raw, dependencies=dependencies)
  111. class MockTestRunner:
  112. def __init__(self, *args, **kwargs):
  113. pass
  114. MockTestRunner.run_tests = mock.Mock(return_value=[])
  115. class ManageCommandTests(unittest.TestCase):
  116. def test_custom_test_runner(self):
  117. call_command('test', 'sites',
  118. testrunner='test_runner.tests.MockTestRunner')
  119. MockTestRunner.run_tests.assert_called_with(('sites',))
  120. def test_bad_test_runner(self):
  121. with self.assertRaises(AttributeError):
  122. call_command('test', 'sites', testrunner='test_runner.NonexistentRunner')
  123. def test_time_recorded(self):
  124. with captured_stderr() as stderr:
  125. call_command('test', '--timing', 'sites', testrunner='test_runner.tests.MockTestRunner')
  126. self.assertIn('Total run took', stderr.getvalue())
  127. class CustomTestRunnerOptionsSettingsTests(AdminScriptTestCase):
  128. """
  129. Custom runners can add command line arguments. The runner is specified
  130. through a settings file.
  131. """
  132. def setUp(self):
  133. super().setUp()
  134. settings = {
  135. 'TEST_RUNNER': '\'test_runner.runner.CustomOptionsTestRunner\'',
  136. }
  137. self.write_settings('settings.py', sdict=settings)
  138. def test_default_options(self):
  139. args = ['test', '--settings=test_project.settings']
  140. out, err = self.run_django_admin(args)
  141. self.assertNoOutput(err)
  142. self.assertOutput(out, '1:2:3')
  143. def test_default_and_given_options(self):
  144. args = ['test', '--settings=test_project.settings', '--option_b=foo']
  145. out, err = self.run_django_admin(args)
  146. self.assertNoOutput(err)
  147. self.assertOutput(out, '1:foo:3')
  148. def test_option_name_and_value_separated(self):
  149. args = ['test', '--settings=test_project.settings', '--option_b', 'foo']
  150. out, err = self.run_django_admin(args)
  151. self.assertNoOutput(err)
  152. self.assertOutput(out, '1:foo:3')
  153. def test_all_options_given(self):
  154. args = ['test', '--settings=test_project.settings', '--option_a=bar',
  155. '--option_b=foo', '--option_c=31337']
  156. out, err = self.run_django_admin(args)
  157. self.assertNoOutput(err)
  158. self.assertOutput(out, 'bar:foo:31337')
  159. class CustomTestRunnerOptionsCmdlineTests(AdminScriptTestCase):
  160. """
  161. Custom runners can add command line arguments when the runner is specified
  162. using --testrunner.
  163. """
  164. def setUp(self):
  165. super().setUp()
  166. self.write_settings('settings.py')
  167. def test_testrunner_option(self):
  168. args = [
  169. 'test', '--testrunner', 'test_runner.runner.CustomOptionsTestRunner',
  170. '--option_a=bar', '--option_b=foo', '--option_c=31337'
  171. ]
  172. out, err = self.run_django_admin(args, 'test_project.settings')
  173. self.assertNoOutput(err)
  174. self.assertOutput(out, 'bar:foo:31337')
  175. def test_testrunner_equals(self):
  176. args = [
  177. 'test', '--testrunner=test_runner.runner.CustomOptionsTestRunner',
  178. '--option_a=bar', '--option_b=foo', '--option_c=31337'
  179. ]
  180. out, err = self.run_django_admin(args, 'test_project.settings')
  181. self.assertNoOutput(err)
  182. self.assertOutput(out, 'bar:foo:31337')
  183. def test_no_testrunner(self):
  184. args = ['test', '--testrunner']
  185. out, err = self.run_django_admin(args, 'test_project.settings')
  186. self.assertIn('usage', err)
  187. self.assertNotIn('Traceback', err)
  188. self.assertNoOutput(out)
  189. class Ticket17477RegressionTests(AdminScriptTestCase):
  190. def setUp(self):
  191. super().setUp()
  192. self.write_settings('settings.py')
  193. def test_ticket_17477(self):
  194. """'manage.py help test' works after r16352."""
  195. args = ['help', 'test']
  196. out, err = self.run_manage(args)
  197. self.assertNoOutput(err)
  198. class SQLiteInMemoryTestDbs(TransactionTestCase):
  199. available_apps = ['test_runner']
  200. databases = {'default', 'other'}
  201. @unittest.skipUnless(all(db.connections[conn].vendor == 'sqlite' for conn in db.connections),
  202. "This is an sqlite-specific issue")
  203. def test_transaction_support(self):
  204. # Assert connections mocking is appropriately applied by preventing
  205. # any attempts at calling create_test_db on the global connection
  206. # objects.
  207. for connection in db.connections.all():
  208. create_test_db = mock.patch.object(
  209. connection.creation,
  210. 'create_test_db',
  211. side_effect=AssertionError("Global connection object shouldn't be manipulated.")
  212. )
  213. create_test_db.start()
  214. self.addCleanup(create_test_db.stop)
  215. for option_key, option_value in (
  216. ('NAME', ':memory:'), ('TEST', {'NAME': ':memory:'})):
  217. tested_connections = db.ConnectionHandler({
  218. 'default': {
  219. 'ENGINE': 'django.db.backends.sqlite3',
  220. option_key: option_value,
  221. },
  222. 'other': {
  223. 'ENGINE': 'django.db.backends.sqlite3',
  224. option_key: option_value,
  225. },
  226. })
  227. with mock.patch('django.test.utils.connections', new=tested_connections):
  228. other = tested_connections['other']
  229. DiscoverRunner(verbosity=0).setup_databases()
  230. msg = (
  231. "DATABASES setting '%s' option set to sqlite3's ':memory:' value "
  232. "shouldn't interfere with transaction support detection." % option_key
  233. )
  234. # Transaction support is properly initialized for the 'other' DB.
  235. self.assertTrue(other.features.supports_transactions, msg)
  236. # And all the DBs report that they support transactions.
  237. self.assertTrue(connections_support_transactions(), msg)
  238. class DummyBackendTest(unittest.TestCase):
  239. def test_setup_databases(self):
  240. """
  241. setup_databases() doesn't fail with dummy database backend.
  242. """
  243. tested_connections = db.ConnectionHandler({})
  244. with mock.patch('django.test.utils.connections', new=tested_connections):
  245. runner_instance = DiscoverRunner(verbosity=0)
  246. old_config = runner_instance.setup_databases()
  247. runner_instance.teardown_databases(old_config)
  248. class AliasedDefaultTestSetupTest(unittest.TestCase):
  249. def test_setup_aliased_default_database(self):
  250. """
  251. setup_databases() doesn't fail when 'default' is aliased
  252. """
  253. tested_connections = db.ConnectionHandler({
  254. 'default': {
  255. 'NAME': 'dummy'
  256. },
  257. 'aliased': {
  258. 'NAME': 'dummy'
  259. }
  260. })
  261. with mock.patch('django.test.utils.connections', new=tested_connections):
  262. runner_instance = DiscoverRunner(verbosity=0)
  263. old_config = runner_instance.setup_databases()
  264. runner_instance.teardown_databases(old_config)
  265. class SetupDatabasesTests(SimpleTestCase):
  266. def setUp(self):
  267. self.runner_instance = DiscoverRunner(verbosity=0)
  268. def test_setup_aliased_databases(self):
  269. tested_connections = db.ConnectionHandler({
  270. 'default': {
  271. 'ENGINE': 'django.db.backends.dummy',
  272. 'NAME': 'dbname',
  273. },
  274. 'other': {
  275. 'ENGINE': 'django.db.backends.dummy',
  276. 'NAME': 'dbname',
  277. }
  278. })
  279. with mock.patch('django.db.backends.dummy.base.DatabaseWrapper.creation_class') as mocked_db_creation:
  280. with mock.patch('django.test.utils.connections', new=tested_connections):
  281. old_config = self.runner_instance.setup_databases()
  282. self.runner_instance.teardown_databases(old_config)
  283. mocked_db_creation.return_value.destroy_test_db.assert_called_once_with('dbname', 0, False)
  284. def test_setup_test_database_aliases(self):
  285. """
  286. The default database must be the first because data migrations
  287. use the default alias by default.
  288. """
  289. tested_connections = db.ConnectionHandler({
  290. 'other': {
  291. 'ENGINE': 'django.db.backends.dummy',
  292. 'NAME': 'dbname',
  293. },
  294. 'default': {
  295. 'ENGINE': 'django.db.backends.dummy',
  296. 'NAME': 'dbname',
  297. }
  298. })
  299. with mock.patch('django.test.utils.connections', new=tested_connections):
  300. test_databases, _ = get_unique_databases_and_mirrors()
  301. self.assertEqual(
  302. test_databases,
  303. {
  304. ('', '', 'django.db.backends.dummy', 'test_dbname'): (
  305. 'dbname',
  306. ['default', 'other'],
  307. ),
  308. },
  309. )
  310. def test_destroy_test_db_restores_db_name(self):
  311. tested_connections = db.ConnectionHandler({
  312. 'default': {
  313. 'ENGINE': settings.DATABASES[db.DEFAULT_DB_ALIAS]["ENGINE"],
  314. 'NAME': 'xxx_test_database',
  315. },
  316. })
  317. # Using the real current name as old_name to not mess with the test suite.
  318. old_name = settings.DATABASES[db.DEFAULT_DB_ALIAS]["NAME"]
  319. with mock.patch('django.db.connections', new=tested_connections):
  320. tested_connections['default'].creation.destroy_test_db(old_name, verbosity=0, keepdb=True)
  321. self.assertEqual(tested_connections['default'].settings_dict["NAME"], old_name)
  322. def test_serialization(self):
  323. tested_connections = db.ConnectionHandler({
  324. 'default': {
  325. 'ENGINE': 'django.db.backends.dummy',
  326. },
  327. })
  328. with mock.patch('django.db.backends.dummy.base.DatabaseWrapper.creation_class') as mocked_db_creation:
  329. with mock.patch('django.test.utils.connections', new=tested_connections):
  330. self.runner_instance.setup_databases()
  331. mocked_db_creation.return_value.create_test_db.assert_called_once_with(
  332. verbosity=0, autoclobber=False, serialize=True, keepdb=False
  333. )
  334. def test_serialized_off(self):
  335. tested_connections = db.ConnectionHandler({
  336. 'default': {
  337. 'ENGINE': 'django.db.backends.dummy',
  338. 'TEST': {'SERIALIZE': False},
  339. },
  340. })
  341. msg = (
  342. 'The SERIALIZE test database setting is deprecated as it can be '
  343. 'inferred from the TestCase/TransactionTestCase.databases that '
  344. 'enable the serialized_rollback feature.'
  345. )
  346. with mock.patch('django.db.backends.dummy.base.DatabaseWrapper.creation_class') as mocked_db_creation:
  347. with mock.patch('django.test.utils.connections', new=tested_connections):
  348. with self.assertWarnsMessage(RemovedInDjango50Warning, msg):
  349. self.runner_instance.setup_databases()
  350. mocked_db_creation.return_value.create_test_db.assert_called_once_with(
  351. verbosity=0, autoclobber=False, serialize=False, keepdb=False
  352. )
  353. @skipUnlessDBFeature('supports_sequence_reset')
  354. class AutoIncrementResetTest(TransactionTestCase):
  355. """
  356. Creating the same models in different test methods receive the same PK
  357. values since the sequences are reset before each test method.
  358. """
  359. available_apps = ['test_runner']
  360. reset_sequences = True
  361. def _test(self):
  362. # Regular model
  363. p = Person.objects.create(first_name='Jack', last_name='Smith')
  364. self.assertEqual(p.pk, 1)
  365. # Auto-created many-to-many through model
  366. p.friends.add(Person.objects.create(first_name='Jacky', last_name='Smith'))
  367. self.assertEqual(p.friends.through.objects.first().pk, 1)
  368. # Many-to-many through model
  369. b = B.objects.create()
  370. t = Through.objects.create(person=p, b=b)
  371. self.assertEqual(t.pk, 1)
  372. def test_autoincrement_reset1(self):
  373. self._test()
  374. def test_autoincrement_reset2(self):
  375. self._test()
  376. class EmptyDefaultDatabaseTest(unittest.TestCase):
  377. def test_empty_default_database(self):
  378. """
  379. An empty default database in settings does not raise an ImproperlyConfigured
  380. error when running a unit test that does not use a database.
  381. """
  382. tested_connections = db.ConnectionHandler({'default': {}})
  383. with mock.patch('django.db.connections', new=tested_connections):
  384. connection = tested_connections[db.utils.DEFAULT_DB_ALIAS]
  385. self.assertEqual(connection.settings_dict['ENGINE'], 'django.db.backends.dummy')
  386. connections_support_transactions()
  387. class RunTestsExceptionHandlingTests(unittest.TestCase):
  388. def test_run_checks_raises(self):
  389. """
  390. Teardown functions are run when run_checks() raises SystemCheckError.
  391. """
  392. with mock.patch('django.test.runner.DiscoverRunner.setup_test_environment'), \
  393. mock.patch('django.test.runner.DiscoverRunner.setup_databases'), \
  394. mock.patch('django.test.runner.DiscoverRunner.build_suite'), \
  395. mock.patch('django.test.runner.DiscoverRunner.run_checks', side_effect=SystemCheckError), \
  396. mock.patch('django.test.runner.DiscoverRunner.teardown_databases') as teardown_databases, \
  397. mock.patch('django.test.runner.DiscoverRunner.teardown_test_environment') as teardown_test_environment:
  398. runner = DiscoverRunner(verbosity=0, interactive=False)
  399. with self.assertRaises(SystemCheckError):
  400. runner.run_tests(['test_runner_apps.sample.tests_sample.TestDjangoTestCase'])
  401. self.assertTrue(teardown_databases.called)
  402. self.assertTrue(teardown_test_environment.called)
  403. def test_run_checks_raises_and_teardown_raises(self):
  404. """
  405. SystemCheckError is surfaced when run_checks() raises SystemCheckError
  406. and teardown databases() raises ValueError.
  407. """
  408. with mock.patch('django.test.runner.DiscoverRunner.setup_test_environment'), \
  409. mock.patch('django.test.runner.DiscoverRunner.setup_databases'), \
  410. mock.patch('django.test.runner.DiscoverRunner.build_suite'), \
  411. mock.patch('django.test.runner.DiscoverRunner.run_checks', side_effect=SystemCheckError), \
  412. mock.patch('django.test.runner.DiscoverRunner.teardown_databases', side_effect=ValueError) \
  413. as teardown_databases, \
  414. mock.patch('django.test.runner.DiscoverRunner.teardown_test_environment') as teardown_test_environment:
  415. runner = DiscoverRunner(verbosity=0, interactive=False)
  416. with self.assertRaises(SystemCheckError):
  417. runner.run_tests(['test_runner_apps.sample.tests_sample.TestDjangoTestCase'])
  418. self.assertTrue(teardown_databases.called)
  419. self.assertFalse(teardown_test_environment.called)
  420. def test_run_checks_passes_and_teardown_raises(self):
  421. """
  422. Exceptions on teardown are surfaced if no exceptions happen during
  423. run_checks().
  424. """
  425. with mock.patch('django.test.runner.DiscoverRunner.setup_test_environment'), \
  426. mock.patch('django.test.runner.DiscoverRunner.setup_databases'), \
  427. mock.patch('django.test.runner.DiscoverRunner.build_suite'), \
  428. mock.patch('django.test.runner.DiscoverRunner.run_checks'), \
  429. mock.patch('django.test.runner.DiscoverRunner.teardown_databases', side_effect=ValueError) \
  430. as teardown_databases, \
  431. mock.patch('django.test.runner.DiscoverRunner.teardown_test_environment') as teardown_test_environment:
  432. runner = DiscoverRunner(verbosity=0, interactive=False)
  433. with self.assertRaises(ValueError):
  434. # Suppress the output when running TestDjangoTestCase.
  435. with mock.patch('sys.stderr'):
  436. runner.run_tests(['test_runner_apps.sample.tests_sample.TestDjangoTestCase'])
  437. self.assertTrue(teardown_databases.called)
  438. self.assertFalse(teardown_test_environment.called)