Back to Blog
Lesson 14 of the Software Testing & Debugging: Testing & Debugging Foundations (QA) course
TestingAugust 1, 20264 min read

First Steps into Unit Testing: Automate Your Quality Assurance

Ready to move beyond manual checks? Learn how to install a testing framework, write your first unit test, and automate your code validation process today.

unit testingpythonpytestautomationquality assurance
Dimly lit caution sign indicating 'Watch Your Step' in urban setting.

Previously in this course, we explored The Testing Pyramid and established why unit tests form the bedrock of a stable, maintainable codebase. Now, we shift from theory to practice: you will install a testing framework, write your first automated test, and run it to verify a piece of project logic.

Unit testing is the practice of testing the smallest "unit" of code—usually a single function or method—in complete isolation. By automating these checks, you create a safety net that warns you immediately when a change breaks existing functionality.

Choosing and Installing a Test Framework

A test framework provides the infrastructure to discover, run, and report on your tests. While there are many options, we will use pytest for this example because it is the industry standard for Python development, offering a simple syntax and powerful features that scale with your project.

To begin, ensure your virtual environment is active (as discussed in Setting Up the Project Environment). Install the framework using your package manager:

Bash
# Install pytest via pip
pip install pytest

Once installed, you can verify it by checking the version:

Bash
pytest --version

Writing Your First Unit Test

Let’s advance our running project. Imagine your project has a utility function that calculates a discount. We want to ensure this function behaves correctly under various conditions.

Create a file named calculator.py with the following logic:

PYTHON
# calculator.py
def apply_discount(price, discount_percent):
    if not (0 <= discount_percent <= 100):
        raise ValueError("Discount must be between 0 and 100")
    return price * (1 - discount_percent / 100)

Now, create a test file named test_calculator.py. By convention, pytest looks for files prefixed with test_ to automatically discover your tests.

PYTHON
# test_calculator.py
from calculator import apply_discount

def test_apply_discount_valid():
    # Arrange (Setup inputs)
    price = 100
    discount = 20
    
    # Act (Call the function)
    result = apply_discount(price, discount)
    
    # Assert (Verify the outcome)
    assert result == 80.0

Running the Test Suite

With your test file saved, run the test suite by executing the pytest command in your terminal. Pytest will scan your directory, find files starting with test_, execute the functions starting with test_, and report the results.

Bash
pytest

You should see an output indicating a "pass" (usually a green dot or a "PASSED" message). If you change the assert value to 70, you will see a "FAILED" report, which provides the exact diff between expected and actual values—a powerful tool for debugging.

Hands-on Exercise

  1. Add a new test function in test_calculator.py called test_apply_discount_invalid.
  2. Use the pytest.raises helper to verify that the function correctly raises a ValueError when the discount is 150. Hint: You will need to import pytest at the top of your file.
  3. Run pytest again to confirm both the original test and your new error-handling test pass.

Common Pitfalls to Avoid

  • Testing too much at once: Keep units small. If a test requires setting up a database or a network connection, it is likely an integration test, not a unit test.
  • Non-deterministic tests: Tests should produce the same result every time. Avoid using random or current system time inside your unit tests, as this leads to "flaky" tests that pass and fail intermittently.
  • Ignoring test naming conventions: If your file isn't named test_*.py or your function isn't named test_*, pytest will skip them entirely. Always verify that your test is actually being executed by the runner.

Frequently Asked Questions

Q: Do I need to write a test for every single line of code? A: Not necessarily. Focus your unit testing efforts on complex logic, edge cases, and business rules. Use Equivalence Partitioning to decide which inputs provide the most value.

Q: What if my test passes, but the code is still broken? A: This usually means your assertion is too broad or you are testing the wrong thing. Always ensure your test fails first (Red-Green-Refactor cycle), which we will cover in later lessons.

Q: Can I run tests in parallel? A: Yes, once your suite grows, you can install pytest-xdist and run pytest -n auto to utilize multiple CPU cores.

Recap

We have successfully installed a testing framework, written a functional test, and verified our logic using assertions. You’ve moved from manual verification to automated, repeatable checks, significantly reducing the risk of regressions in your codebase.

Up next: We will dive into specific assertion types to validate state changes and return values with greater precision.

Similar Posts