Back to Blog
Lesson 7 of the CI/CD: Continuous Integration from Scratch course
DevOpsJuly 7, 20264 min read

Introduction to Automated Testing: Building Reliable Code

Learn the essentials of unit testing to improve code quality. Create a simple Python script and build a test suite to automate your verification process.

unit testingci/cdpythonautomationprogrammingbeginner

Previously in this course, we covered workflow syntax deep dive. Now that you can execute arbitrary commands in your pipeline, it's time to ensure that the code you're running actually works.

In the world of professional software engineering, manual verification is the enemy of velocity. You cannot afford to open a browser and click through every feature every time you change a line of code. This lesson focuses on unit testing, the practice of verifying the smallest testable parts of your application in isolation.

Why Unit Testing Matters for Code Quality

At its core, unit testing is about feedback. If you make a mistake, you want to know about it seconds after saving your file, not hours later when a user reports a crash. By writing tests, you are essentially creating a safety net. This net allows you to refactor, upgrade dependencies, and add features with the confidence that you haven't broken existing functionality.

When we talk about automation in CI/CD, we aren't just talking about running a script; we are talking about running a validation suite that acts as a gatekeeper. If the tests don't pass, the build doesn't move forward.

Creating Your First Application

Let's start by creating a simple calculator script. It’s a classic example because it’s easy to verify. Create a file named calculator.py in your project folder:

PYTHON
# calculator.py

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

def subtract(a, b):
    return a - b

This is our "production" code. It’s clean, modular, and does exactly one thing. Note how we avoided putting print statements or complex logic inside the functions—this makes them "pure" and easy to test.

Writing the Test Suite

To test this, we’ll use Python’s built-in unittest framework. It’s perfect for beginners because it requires no extra installation. Create a file named test_calculator.py:

PYTHON
# test_calculator.py
import unittest
from calculator import add, subtract

class TestCalculator(unittest.TestCase):

    def test_add(self):
        self.assertEqual(add(1, 2), 3)
        self.assertEqual(add(-1, 1), 0)

    def test_subtract(self):
        self.assertEqual(subtract(10, 5), 5)
        self.assertEqual(subtract(0, 5), -5)

if __name__ == CE9178">'__main__':
    unittest.main()

Running Your Tests Locally

Before we put this into GitHub Actions, verify it works on your machine. Open your terminal and run:

Bash
python3 test_calculator.py

You should see an output like OK. This confirms your tests passed. If you change a value in the assertEqual check, you will see a failure trace. This "red-green" cycle—where you see a test fail, then fix the code to make it pass—is the heartbeat of modern development.

Hands-on Exercise

  1. Modify the code: Open calculator.py and add a multiply(a, b) function.
  2. Add a test: Update test_calculator.py to include a new method test_multiply that asserts the result of multiply(3, 4) is 12.
  3. Run again: Execute python3 test_calculator.py to ensure your new test passes.

Common Pitfalls

  • Testing Implementation, Not Behavior: Don't write tests that check how a function works (e.g., checking internal variable names). Test what it returns given specific inputs.
  • The "Slow" Test: Unit tests should be lightning fast. If your test needs to hit a database or a live API, it isn't a unit test—it's an integration test. Keep your unit tests focused on pure logic.
  • Ignoring Failure: Never ignore a failing test. As noted in our discussion on technical debt, a failing test is a "broken window." Fix it immediately, or developers will stop trusting the test suite entirely.

FAQ

Q: Do I need a specific framework? A: Not for learning. Python’s unittest or Node's assert module is fine. As you scale, you might explore pytest for Python or Jest for Node.js, as seen in this guide on testing with Jest.

Q: How many tests should I write? A: Focus on "happy paths" (normal inputs) and "edge cases" (like dividing by zero or empty strings). Aim for high coverage, but prioritize testing the logic that is most likely to break.

Recap

We’ve moved from just running scripts to validating them. By isolating logic in calculator.py and verifying it with test_calculator.py, you've established the foundation for automated quality control. In the next lesson, we will wire this into our CI pipeline to ensure these tests run automatically every time you commit code.

Up next: Running Tests in CI

Similar Posts