Python’s unittest(Python Document) module, part of the standard library, is designed to support unit test.

Unit test is essential for verifying that your code behaves as expected, and unittest provides all the necessary tools to write, run, and validate these tests effectively. By incorporating unittest into your development workflow, you can catch bugs early and ensure your code remains reliable as it evolves.

  1. Standardized Structure:
    Write tests using consistent class structures, making your test suite easier to read and maintain.
  2. Test Automation:
    Run all your written tests at once to quickly verify functionality and catch issues.
  3. Test Result Reporting:
    Receive clear and detailed outputs for successes, failures, and errors, helping you identify problems efficiently.
  4. Modularity:
    Execute specific tests or groups of tests as needed, providing flexibility for targeted debugging and validation.

However, it’s hard to do all unit tests using this function alone, so in the next post I’ll write about one of the advanced features, Mock.

Key Methods

  • assertEqual(a, b): checks if a == b.
  • assertNotEqual(a, b): Check if a != b.
  • assertTrue(x): Checking that x is true.
  • assertFalse(x): Checking if x is false.
  • assertIs(a, b): Check if a is b.
  • assertIsNot(a, b): Check that a is not b.
  • assertRaises(exception, callable, …): Checking if an exception is raised.

setUp / tearDown

A method that runs before and after each test. This is useful for initializing or cleaning up resources that are used repeatedly.

Unit Test Code

#pip install unittest

import unittest

class Calculator:
    def add(self, a, b):
        return a + b

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

    def multiply(self, a, b):
        return a * b

    def divide(self, a, b):
        if b == 0:
            raise ValueError("Cannot divide by zero")
        return a / b

class PlusTest(unittest.TestCase):
    def setUp(self):
        self.calc = Calculator()

    def test_add(self):
        self.assertEqual(self.calc.add(1, 1), 2)
        self.assertNotEqual(self.calc.add(-1, 1), 1)

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

    def test_multiply(self):
        self.assertEqual(self.calc.multiply(3, 7), 21)

    def test_divide(self):
        self.assertEqual(self.calc.divide(10, 2), 5)
        with self.assertRaises(ValueError):
            self.calc.divide(10, 0)


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

"""
assertEqual(a, b) : a is b
assertNotEqual(a, b): a is not b
assertTure(x): x is true
assertIs(a, b): object a in b
assertIn(a, b): b in a
"""

Output

....
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK

While most developers agree on the principles of Test-Driven Development (TDD), not many actively incorporate it into their workflow. One reason could be that they haven’t experienced the speed and efficiency TDD can bring to development.

This hesitation isn’t necessarily about experience or knowledge—whether one is a senior or junior developer. The real challenge lies in deciding whether to plan ahead or react to immediate maintenance needs. Time constraints and external pressures often lead companies to prioritize quick releases over careful planning.

The reality is that launching a product to market and reacting to feedback often feels more accurate and practical than trying to predict its success beforehand.

A Short Story From Experience
I once worked on a small product with about 300,000 lines of code and around 60 features. When we first released it, porting each new feature took about two weeks. The codebase was chaotic, full of “spaghetti code,” with function names like a_b_1 that made maintenance a nightmare.

Eventually, we decided to rebuild the product from the ground up in the same language. With 4 developers, it took 1 year and 2 months to rewrite the same features. The revamped codebase was streamlined to 150,000 lines, supported by over 12,000 unit tests.

The result? Porting a single feature release dropped to just 1–2 days, with side effects completely eliminated.

This experience demonstrated that while TDD and proper planning might seem time-consuming initially, the long-term benefits in efficiency, maintainability, and reliability are undeniable.

But would a development company give a developer a year of unproductive time?

Challenges of Rebuilding in the Real World

In most companies, rebuilding a product from scratch isn’t a common scenario. For one project I worked on, we split a 12-person team into two groups: 8 people continued maintaining the existing product, while 4 of us worked to convince the company to move forward with a rebuild.

Ironically, as the rebuild progressed and maintenance became easier and faster, most developers on the original team were reassigned to other projects. However, the individual skills of those involved improved significantly.

This experience gave me a new perspective on why developers involved in open-source projects are often so skilled. Open-source contributors have the unique opportunity to refine and improve their work over time, often at their own pace. This iterative process allows them to deeply understand their code and continually enhance their abilities.

It’s a reminder that time spent improving and refining existing work is as valuable—if not more so—than creating something new from scratch.

By Mark

-_-

Leave a Reply

Your email address will not be published. Required fields are marked *