keepcalculator

06 Numbers and Powers

How to Write Unit Tests for Python Classes

Card No. 06·52020 · Updated · Standard formula, shown below

Unit testing is a way to verify that individual parts of your code work as expected. When you write tests for a Python class, you check that its methods return correct values, handle edge cases, and fail gracefully when given bad input. This guide walks you through writing tests using the built-in unittest framework, which requires no extra installation. You’ll learn how to structure tests, what to test, and how to avoid common pitfalls.

What You’ll Need

  • Python 3 installed on your machine (any recent version works).
  • A text editor or IDE (VS Code, PyCharm, or even Notepad).
  • The class you want to test, saved in a .py file (e.g., calculator.py).
  • A basic understanding of Python classes and methods.

Step-by-Step

  1. Create a separate test file. Name it test_<your_module>.py (e.g., test_calculator.py). This keeps tests isolated from your source code and makes it easy to run them with test runners.
  2. Import the class you want to test. At the top of the test file, import the class from its module. If your class is in calculator.py, write from calculator import Calculator. Make sure the test file is in the same directory or adjust the import path.
  3. Import the unittest module. Add import unittest at the top. This gives you access to TestCase and assertion methods.
  4. Create a test class that inherits from unittest.TestCase. For example: class TestCalculator(unittest.TestCase):. This class will group all tests for the Calculator class.
  5. Write test methods inside the test class. Each method should start with the word test_ (e.g., test_add). This naming convention lets unittest automatically discover and run them.
  6. Set up objects in setUp if needed. If your class requires initialization, create an instance in a setUp(self) method so each test starts with a fresh object. For example: def setUp(self): self.calc = Calculator(). This avoids repeating code and prevents state from leaking between tests.
  7. Use assertion methods to check expected behavior. Common ones: assertEqual(a, b) checks equality, assertTrue(x) checks if x is True, assertFalse(x) checks if x is False, assertRaises(SomeException, callable, *args) checks that an exception is raised.
  8. Test each method with normal inputs first. For example, if you have an add(a, b) method, test self.assertEqual(self.calc.add(2, 3), 5).
  9. Test edge cases and invalid inputs. Try zero, negative numbers, very large numbers, or wrong types. Use assertRaises to verify that the method raises a ValueError or TypeError when appropriate.
  10. Test methods that modify state. If your class has a counter or a list that changes, call the method and then assert the new state. For example, self.calc.increment() then assertEqual(self.calc.count, 1).
  11. Run your tests. In the terminal, navigate to the directory containing your test file and run python -m unittest test_calculator.py. You can also run python -m unittest discover to find all test files in the current directory.
  12. Read the output. If all tests pass, you’ll see a dot for each test and an OK message. If any fail, you’ll see an F and a detailed traceback showing which assertion failed and why.

Common Mistakes

  • Not starting test method names with test_ – unittest will silently skip them, and you’ll get a false sense of security.
  • Testing multiple things in one test method – if one assertion fails, you won’t know if the others would have passed. Keep each test focused on one behavior.
  • Forgetting to import the class or module – this causes a ModuleNotFoundError. Double-check your import paths.
  • Using assert instead of self.assert* methods – plain assert works, but unittest’s methods give better failure messages and integrate with the runner.
  • Sharing mutable state between tests – if you create an object in setUp and modify it, the next test may see the modified version. Use setUp to create fresh instances each time.
  • Not testing invalid inputs – your class may crash on bad data; tests should catch that.

Tips

  • Write tests before or right after writing the class – test-driven development (TDD) helps you design cleaner interfaces.
  • Use descriptive test method names like test_add_negative_numbers so failures are easy to understand.
  • Keep tests independent – each test should not depend on the order of execution.
  • Use setUp for repetitive initialization, but avoid doing heavy work there – it runs before every test, so keep it fast.
  • Run tests frequently – after every change to your class, run the test suite to catch regressions early.
  • Use assertAlmostEqual for floating-point comparisons – floating-point arithmetic can have tiny rounding errors, so assertEqual(0.1 + 0.2, 0.3) will fail. Use assertAlmostEqual(0.1 + 0.2, 0.3, places=7).

Troubleshooting

  • No tests are run (0 tests) – Check that your test file is named starting with test_ or ending with _test.py. Also ensure your test methods start with test_.
  • ImportError: No module named ‘your_module’ – Make sure the test file is in the same directory as the module, or add the parent directory to sys.path. You can also run python -m unittest from the root of your project.
  • Tests pass but you know there’s a bug – You might be testing the wrong behavior. Review your assertions to ensure they match what the method should actually do. Also check for side effects that aren’t being tested.
  • AssertionError with floating-point numbers – Use assertAlmostEqual instead of assertEqual for non-integer results.
  • Tests fail because of side effects from previous tests – Ensure each test creates its own instance in setUp or at the start of the test. Avoid using class-level mutable attributes.
  • Running from a different directory causes ‘no module named’ – Use python -m unittest discover -s . from the project root, or set PYTHONPATH.

Frequently Asked Questions

Do I need to install anything to use unittest?

No, unittest is part of the Python standard library. Just import unittest and you’re ready.

Can I use pytest instead of unittest?

Yes, pytest is a popular third-party framework that often requires less boilerplate. However, unittest is built-in and works fine for most projects. You can also run unittest tests with pytest by installing pytest.

How do I test a class that requires external resources like a database?

You can use mocking to replace the external dependency with a fake. The unittest.mock module provides patch and MagicMock for this purpose.

What is the difference between setUp and setUpClass?

setUp runs before each test method, giving you a fresh state. setUpClass runs once before all tests in the class, which is useful for expensive setup that can be shared, but be careful with mutable state.

Should I test private methods (those starting with underscore)?

Generally, no. Test the public interface. Private methods are implementation details and may change. If you feel the need to test them, consider refactoring them into separate classes or functions.

How many tests should I write?

Aim for at least one test per public method, plus tests for edge cases and error handling. Quality matters more than quantity; a few well-targeted tests are better than dozens of trivial ones.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →