If your breakpoints aren't working and you use pytest-cov, run your tests with:
pytest your_tests.py --no-cov
You're debugging a Django application with pytest, and your breakpoints aren't stopping where they should. If this is happening to you, you're not alone - there's a known issue between coverage
, pytest-cov
, and factory_boy
that can cause breakpoints to behave unexpectedly.
This issue affects:
coverage
versions 6.4.1 and laterpytest-cov
(all recent versions)Let's walk through a typical scenario where this issue appears. Consider a simple Django user model with a full_name
property:
class User(AbstractUser): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) @property def full_name(self): return f"{self.first_name} {self.last_name}"
Using Factory Boy (a fantastic tool for test data generation), we've created a factory:
class UserFactory(factory.django.DjangoModelFactory): first_name = factory.Faker("first_name") last_name = factory.Faker("last_name") username = factory.LazyAttributeSequence( lambda a, n: f"{a.first_name}{a.last_name}{n}".lower() ) class Meta: model = User
Now, let's write a simple test:
class TestUser(TestCase): def test_full_name_method(self): """Test the full_name property returns the expected value""" user = UserFactory.create( first_name="Bob", last_name="Roberts" ) breakpoint() # This should stop here, but might not! self.assertEqual(user.full_name, "Bob Roberts")
When you run this test with pytest-cov enabled, something strange happens:
breakpoint()
line in your testfull_name
property definition insteadHere's what you might see:
--Call--
> /path/to/your/app/models.py(XX)full_name()
-> @property
(Pdb) print(user)
*** NameError: name 'user' is not defined
The simplest solution is to disable coverage when you need to use breakpoints:
# Option 1: Disable coverage completely pytest your_tests.py --no-cov # Option 2: Run specific test with no coverage pytest your_tests.py::TestUser::test_full_name_method --no-cov
If you're using PyCharm, you can permanently configure this in your debug settings:
--no-cov
to the "Additional Arguments" fieldIt is worth noting that you could downgrade coverage far enough that the issue goes away - but that is not a practical solution.
If you need to maintain coverage while debugging:
def test_full_name_method(self): user = UserFactory.create(first_name="Bob", last_name="Roberts") # Option 1: Use print statements print(f"Debug: user object = {user}") print(f"Debug: full name = {user.full_name}") # Option 2: Use logging import logging logging.debug(f"User object: {user}") self.assertEqual(user.full_name, "Bob Roberts")
This issue typically only affects local development since you generally don't use breakpoints in CI/CD pipelines. Your coverage reports in CI/CD should continue to work normally.
The issue stems from how coverage instrumentation interacts with Python's debugging hooks. When coverage is enabled:
While this issue can be frustrating, the --no-cov
workaround provides a reliable solution. Until the underlying interaction between coverage and debugging is resolved (which may never happen), this remains the most practical approach for local development debugging.
Remember:
--no-cov
when you need breakpoint debuggingHappy debugging!