Python Unittest for Beginners: Unit Tests for a Function to Calculate the Area of a Circle
Tags: python, testing, unittest, tutorial
Published: 2023-02-05 20:17:33

In this article, we will go over the process of writing unit tests for a function that calculates the area of a circle in Python. We will use the `unittest` framework to verify that the function behaves correctly in various scenarios.

## Area of a Circle Function

The function we will be testing is `area_of_circle(r)`, which takes in a radius `r` and returns the area of a circle with that radius. The formula for the area of a circle is `πr^2`, where `π` is the mathematical constant `3.14159`.

```python
import math

def area_of_circle(radius):
    if type(radius) not in [int, float]:
        raise TypeError("Radius must be a number")
    if radius < 0:
        raise ValueError("Radius must be non-negative")
    return math.pi * radius**2
```

The `area_of_circle` function checks the type of the input radius to make sure it is a number, and then raises a `TypeError` if it is not. It also checks if the radius is negative and raises a `ValueError` if it is. If the radius is valid, the function returns the area of the circle.

## Unit Tests

Next, we will write a series of tests using the `unittest` framework to verify that the `area_of_circle` function behaves as expected. Here's the code:

```python
import unittest
import math

class TestAreaOfCircle(unittest.TestCase):
    def test_areas_when_radius_gte_0(self):
        self.assertAlmostEqual(area_of_circle(0), 0)
        self.assertAlmostEqual(area_of_circle(1), math.pi)
        self.assertAlmostEqual(area_of_circle(2), 4 * math.pi)
    
    def test_value_errors_raised(self):
        with self.assertRaises(ValueError):
            area_of_circle(-1)
    
    def test_type_errors_raised(self):
        with self.assertRaises(TypeError):
            area_of_circle("abc")

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

The tests are contained within a class that inherits from `unittest.TestCase`. The class has three test methods:

1. `test_areas_when_radius_gte_0` checks if the areas are calculated correctly when the radius is non-negative.
2. `test_value_errors_raised` verifies that a `ValueError` is raised when the radius is negative.
3. `test_type_errors_raised` checks if a `TypeError` is raised when the radius is not a number.

Each test method uses the `assertAlmostEqual` method to check if the calculated area is equal to the expected value, within a certain tolerance. The `assertRaises` method is used to verify that exceptions are raised when expected.

## Running the Tests

To run the tests, we can simply run the file:

```bash
python test_area_of_circle.py
```

The output should look like this:

```bash
$ python test_area_of_circle.py
...
--------------------------------------------------------
Ran 3 tests in 0.000s

OK
```

The `...` indicates that the tests are running. The `OK` at the end indicates that all the tests passed.

## Summary

By following these steps, you can write unit tests for a function that calculates the area of a circle in Python. You can modify the code to test other functions, and it can be used as a foundation for more complex testing tasks. The full code can be found [here](https://github.com/Rusydy/selenium-learn/blob/main/unittest/basic_test_cal_circle.py)

> Q&A

- What is the difference between `assertEqual` and `assertAlmostEqual`?
  - `assertEqual` checks if two values are equal. `assertAlmostEqual` checks if two values are equal within a certain tolerance. For example, `assertEqual(1.0, 1.0000000000000001)` will fail, but `assertAlmostEqual(1.0, 1.0000000000000001)` will pass.

- What is `assertRaises`?
  - `assertRaises` is a context manager that verifies that an exception is raised when expected. It is used to verify that the function raises the correct exception when the input is invalid.

- Why this `class TestAreaOfCircle(unittest.TestCase)` written like that?
  - The code class `TestAreaOfCircle(unittest.TestCase)` creates a new class named `TestAreaOfCircle` that inherits from `unittest.TestCase`. This is a convention in the unittest framework to create test cases. By inheriting from `TestCase`, the class gains access to a set of testing methods that can be used to write tests.

- Why we put several `def` inside `class`?
  - The `class` contains several `def` that define the test methods. Each test method is responsible for verifying a specific behavior of the function.

- What is `self`?
  - `self` is a special variable that refers to the current instance of the class. It is used to access variables and methods that belong to the class.

- What is `self.assertAlmostEqual`?
  - `self.assertAlmostEqual` is a method that is inherited from `unittest.TestCase`. It is used to check if the calculated area is equal to the expected value, within a certain tolerance.

- What is `unittest.main()`?
  - `unittest.main()` is used to run the tests. It is used to run the tests when the file is run directly. It is not needed when the tests are run using a test runner.

- What is `__name__ == '__main__'`?
  - `__name__` is a special variable that contains the name of the current module. When a module is run directly, `__name__` is set to `__main__`. This is used to prevent the code from being run when the module is imported.