Testing¶
Django's test tools work unchanged: manage.py test, TestCase,
TransactionTestCase, pytest-django, assertNumQueries().
Async code inside TestCase¶
TestCase wraps each test in a sync atomic() block, and runs async tests
with async_to_sync(). Async code there must see the block's writes, and its
own writes must belong to the block. So:
- djox's queries find the open transaction in the current context and run on its connection, on a worker thread.
- Unrelated async code (another request) doesn't see it, and keeps using djox's own connections.
assertNumQueries()counts them: they are logged on the transaction's connection.djox.atomic()inside a test joins the test's transaction as a savepoint.
from django.test import TestCase
from shop.models import Book
class BookTests(TestCase):
async def test_long_books(self):
await Book.objects.acreate(title="Dune", pages=412)
self.assertEqual(await Book.objects.filter(pages__gt=400).acount(), 1)
Test databases on SQLite are files¶
Django tests SQLite on an in-memory database, which only one connection can see. djox has several connections, so:
- The test database is
test_db.sqlite3next toNAME(ordjox_test_<alias>.sqlite3in the temp folder whenNAMEis":memory:").TEST["NAME"]still wins. - It costs little: with WAL mode, a commit doesn't wait for the disk. A RAM
disk (
TEST = {"NAME": "/dev/shm/test_db.sqlite3"}on Linux) is only about 10% faster.
Good to know¶
- Raw SQL in a
TestCase:djox.connections[alias]used directly doesn't see the test's transaction (djox's QuerySets do). UseTransactionTestCase, or@pytest.mark.django_db(transaction=True). - pytest-asyncio tests: inside a sync
atomic()block run by the event loop's own thread (a pytest-asyncio test withouttransaction=True, or anasyncio.run()insideatomic()), nothing else can use the transaction's connection, sodjox.atomic()raisesTransactionManagementError. Usetransaction=True, or Django'sTestCase.