Write Python Tests That Hurt Less
Learn practical tricks to make testing in Python less painful: start with small wins, use pytest fixtures and mocking sparingly, and test what matters.
Testing is one of those things every developer knows they should do, but many of us put it off because it feels like a chore. You finish writing a cool feature, and then the last thing you want to do is spend another hour writing tests that feel clunky and fragile. But at PythonSkillset, we believe testing doesn't have to be painful. Let me show you a few practical tricks that make testing in Python almost enjoyable.
Start with Small Wins
The biggest mistake I see is people trying to test everything at once. Instead, pick one small function—something like a date parser or a simple calculator—and write a test for it first. When that passes, you get a little dopamine hit that makes you want to keep going.
Here's a tiny example that changed how I approach testing:
def calculate_discount(price, discount_percent):
"""Return discounted price. Discount_percent is 0-100."""
if discount_percent < 0 or discount_percent > 100:
raise ValueError("Discount must be between 0 and 100")
return price * (1 - discount_percent / 100)
A test for this could be as simple as:
def test_discount_basic():
assert calculate_discount(100, 20) == 80.0
See how easy that is? No frameworks needed, just pure Python and a simple assert. That's your starting point.
Use pytest for Real Comfort
Once you get comfortable, switch to pytest. It's not scary—it's actually a relief compared to the built-in unittest. With pytest, you don't need to write classes or call self.assertEqual(). Just write functions that start with test_ and use plain assert.
# test_discounts.py
from your_module import calculate_discount
def test_discount_zero():
assert calculate_discount(100, 0) == 100.0
def test_discount_full():
assert calculate_discount(100, 100) == 0.0
def test_discount_invalid():
import pytest
with pytest.raises(ValueError):
calculate_discount(100, -10)
That's it. Run pytest in your terminal and watch it work. No ceremony, just results.
Fixtures Are Your Friend
When I first saw pytest fixtures, I thought they were complicated. But they're actually just functions that set up data for your tests. They save you from repeating yourself.
Imagine you test a user registration system. Instead of creating a new user in every test, you write a fixture once:
import pytest
@pytest.fixture
def sample_user():
return {"name": "Alice", "email": "alice@pythonskillset.com"}
def test_user_email(sample_user):
assert "@" in sample_user["email"]
def test_user_name(sample_user):
assert len(sample_user["name"]) > 0
The fixture runs fresh for every test, so you never share state by accident. This is one of those "aha" moments that makes testing feel clean.
Mocking: Don't Mock Everything
A lot of beginners think they need to mock everything—database calls, API requests, file reads. But mocking too much makes tests brittle and slow. At PythonSkillset, we've seen teams waste hours debugging tests that fail because a mock was slightly wrong.
Instead, test as much as you can with real data. If you're using a local database for development, let your tests connect to it. If you're hitting an API, mock only the external service, not your internal logic.
Here's a balanced approach:
from unittest.mock import patch
def test_fetch_user_data():
with patch("your_module.requests.get") as mock_get:
mock_get.return_value.json.return_value = {"id": 42, "name": "Bob"}
result = fetch_user_data(42)
assert result["name"] == "Bob"
Notice I only mocked the network call, not the entire function. That keeps the test meaningful.
Test What Matters, Not Everything
If you're building a text editor, don't test that every single keystroke works. Test the core logic: opening files, saving changes, searching for text. The edge cases will find you later.
A good rule of thumb: write tests for functions that have conditional logic, math, or branching. Simple getters and setters usually don't need tests.
Run Tests Automatically
The last trick is to make testing effortless. Use a watcher tool like pytest-watch or configure your IDE to run tests on save. When tests run without you thinking about it, you'll catch bugs early and feel confident about changes.
Keep It Simple
Testing doesn't need to be a formal, rigid process. Start small, use tools that feel good, and remember: a bad test is still better than no test. Your future self—and your teammates—will thank you.
Now go write a test for that one function you've been avoiding. You know the one.
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.