Testing in Python – Unit Testing, PyTest & Debugging Complex Code

 

Testing in Python – Unit Testing, PyTest & Debugging Complex Code

Every developer loves writing new features. But the real professionalism of a developer shows in how they test their code.

In real-world projects, especially backend systems, testing is not optional it’s essential. Whether you are building APIs, automation scripts, or data pipelines, testing ensures your code works today and continues to work tomorrow.

In this blog, let’s understand testing in Python in a practical and human way without unnecessary complexity.

Why Testing Matters

Imagine this situation:You fix one bug… and accidentally break three other features.

Without tests, you may not even realize it until production fails.

Testing helps you:

  • Catch bugs early

  • Refactor safely

  • Improve code quality

  • Build confidence in deployments

  • Work better in teams

Good developers write code. Great developers write tested code.

1️⃣ Unit Testing in Python

Unit testing means testing small parts of your code usually individual functions or methods.

Python comes with a built-in module called unittest.

Example: Simple Function

def add(a, b):
return a + b

Unit Test Using unittest

import unittest
from myfile import add

class TestAddFunction(unittest.TestCase):
def test_add_positive_numbers(self):
self.assertEqual(add(2, 3), 5)

def test_add_negative_numbers(self):
self.assertEqual(add(-1, -1), -2)

if __name__ == "__main__":
unittest.main()

This ensures your function behaves correctly for different inputs.

2️⃣ PyTest – A More Powerful & Cleaner Approach

While unittest is powerful, many developers prefer pytest because it is:

  • Simpler

  • More readable

  • Less boilerplate

  • More flexible

Same Example Using PyTest

from myfile import add

def test_add_positive():
assert add(2, 3) == 5

def test_add_negative():
assert add(-1, -1) == -2

That’s it. No classes. No complex setup.

Run it using:  pytest

PyTest automatically discovers test files and runs them.

3️⃣ Testing APIs (Real-World Scenario)

If you're building backend systems using Django or FastAPI, testing APIs becomes critical.

With pytest, you can test:

  • Response status codes

  • JSON output

  • Authentication

  • Error handling

Example idea:

def test_login_api(client):
response = client.post("/login", json={
"username": "admin",
"password": "password123"
})
assert response.status_code == 200

This ensures your login system works as expected.

4️⃣ Mocking – Testing Without Real Dependencies

In real projects, your code may depend on:

  • External APIs

  • Databases

  • Payment gateways

  • File systems

You don’t want to call real services during testing.

That’s where mocking comes in.

PyTest and unittest both support mocking so you can simulate responses instead of making real API calls.

This makes your tests:

  • Faster

  • More reliable

  • Independent

5️⃣ Debugging Complex Code in Python

Testing helps prevent bugs — but debugging helps fix them.

Python provides excellent debugging tools.

Using print() (Beginner Level)

Sometimes simple print statements help trace problems quickly.

But for complex systems, this is not enough.

Using pdb – Python Debugger

Python has a built-in debugger called pdb.

import pdb

def divide(a, b):
pdb.set_trace()
return a / b

When execution reaches set_trace(), you can:

  • Inspect variables

  • Step line by line

  • Continue execution

  • Evaluate expressions

This is extremely useful for complex logic.

Using IDE Debuggers

Modern IDEs like:

  • PyCharm

  • Visual Studio Code

provide graphical debugging tools where you can:

  • Set breakpoints

  • Inspect variables visually

  • Track call stack

  • Monitor memory

This is very helpful in large backend applications.

6️⃣ Writing Good Tests – Best Practices

Here are some practical tips:

✅ Test behavior, not implementation

Focus on what the function should do — not how it does it.

✅ Keep tests simple and readable

If your test is too complex, debugging becomes harder.

✅ One test = one responsibility

Each test should verify one specific behavior.

✅ Use meaningful test names

Bad: test1()
Good: test_login_fails_with_wrong_password()

✅ Automate testing in CI/CD

Integrate tests with GitHub Actions or other CI tools to run automatically before deployment.

Common Mistakes Beginners Make

  • Writing code first, testing later (or never)

  • Not testing edge cases

  • Ignoring error handling

  • Testing manually instead of automating

  • Not mocking external dependencies

Testing is not extra work it is part of development.

If you want to grow as a backend or software developer, testing is a non-negotiable skill.

Frameworks like unittest and pytest make testing in Python simple and powerful. Combine that with proper debugging techniques, and you’ll build reliable, production-ready applications.

Remember:

Untested code is a hidden bug waiting for production.

Start small:

  • Write tests for utility functions

  • Add tests to your Django APIs

  • Practice debugging complex logic

  • Integrate testing into every project

Over time, testing becomes a habit and that habit separates average developers from professional engineers.

Comments