Python Unit Tests & Classes - Practice

QTM 350: Data Science Computing

Davi Moreira

Unit Tests & Classes

In [1]:
import math

Exercises

Exercise

The function area() accepts the argument radius and calculates the area of a circle. Write three tests using assert statements for the following conditions:

  1. Assert that area(1) returns a float;
  2. Assert that area(0) returns a value of 0;
  3. Assert that area(5) is approximately equal to 78.5 (hint: math.isclose(..., abs_tol=0.1))
In [2]:
def area(radius):
    """Calculate the area of a circle based on the given radius."""
    return math.pi * radius ** 2
In [3]:
# Your answer here.

Exercise

In the spirit of the EAFP (easier to ask for forgiveness than permission) philosophy. Modify the code of the function area() and add a try/except statement to catch the type error raised by passing a string to area() as shown below:

In [4]:
area('10')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-28e1bc493b84> in <module>
----> 1 area('10')

<ipython-input-2-13e66cca8177> in area(radius)
      1 def area(radius):
      2     """Calculate the area of a circle based on the given radius."""
----> 3     return math.pi * radius ** 2

TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
In [5]:
def area(radius):
    """Calculate the area of a circle based on the given radius."""
    pass # Remove this line and add your answer here.

Exercise

In the spirit of the LBYL (look before you leap) philosophy. Modify the code of the function area() and add a conditional if/else statement to make sure that a user has passed a number (int or float) to the area() function. If they pass something else, raise a TypeError.

In [6]:
def area(radius):
    """Calculate the area of a circle based on the given radius."""
    pass # Remove this line and add your answer here.

Exercise

For this exercise I want you to create a class called Circle. It should have the following characteristics:

  1. It should be initiated with the argument radius and store this as an instance attribute.
  2. Have a method area() which calculates the area of the circle.
  3. Have a method circumference() which calculates the circumference of the circle.
  4. Have the method __str__() which is a special method in Python and controls what is output to the screen when you print() an instance of your class (learn more here). The print() statement should print the string f"A Circle with radius {self.radius}".

I've provided some tests for you to check your class.

In [7]:
class Circle:
    """A circle with a radius r."""

    pass # Remove this line and add your answer here.
In [8]:
assert Circle(3).radius == 3, "Test 1 failed."
assert math.isclose(Circle(3).area(), 28.3, abs_tol=0.1), "Test 2 failed."
assert math.isclose(Circle(3).circumference(), 18.8, abs_tol=0.1), "Test 3 failed."
assert Circle(3).__str__() == "A Circle with radius 3", "Test 4 failed."
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-5dbdbcaa4346> in <module>
----> 1 assert Circle(3).radius == 3, "Test 1 failed."
      2 assert math.isclose(Circle(3).area(), 28.3, abs_tol=0.1), "Test 2 failed."
      3 assert math.isclose(Circle(3).circumference(), 18.8, abs_tol=0.1), "Test 3 failed."
      4 assert Circle(3).__str__() == "A Circle with radius 3", "Test 4 failed."

TypeError: Circle() takes no arguments

Exercise

Now, let's create a new class sphere that inherits from the circle class we created above. It should have the following characteristics:

  1. It should be initiated exactly the same as Circle was, with the single argument radius which is stored as an instance attribute.
  2. Have a method volume() which calculates the volume of the sphere ($\frac{4}{3}{\pi}{r^3}$).
  3. Outputs the string f"A Sphere with volume 4.19" when you call print(Sphere(1)) (hint: recall the __str__() method from the previous question).

I've provided some tests for you to check your class.

In [9]:
# Your answer here.
In [10]:
assert Sphere(3).radius == 3, "Test 1 failed."
assert math.isclose(Sphere(3).area(), 28.3, abs_tol=0.1), "Test 2 failed."
assert math.isclose(Sphere(3).circumference(), 18.8, abs_tol=0.1), "Test 3 failed."
assert math.isclose(Sphere(3).volume(), 113.1, abs_tol=0.1), "Test 3 failed."
assert Sphere(1).__str__() == "A Sphere with volume 4.19", "Test 4 failed."
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-10-605d8a1c6bb6> in <module>
----> 1 assert Sphere(3).radius == 3, "Test 1 failed."
      2 assert math.isclose(Sphere(3).area(), 28.3, abs_tol=0.1), "Test 2 failed."
      3 assert math.isclose(Sphere(3).circumference(), 18.8, abs_tol=0.1), "Test 3 failed."
      4 assert math.isclose(Sphere(3).volume(), 113.1, abs_tol=0.1), "Test 3 failed."
      5 assert Sphere(1).__str__() == "A Sphere with volume 4.19", "Test 4 failed."

NameError: name 'Sphere' is not defined

Exercise

Imagine that users of our Sphere class often want to instantiate our class with a circumference instead of a radius. Add a class method called from_circ() to the Sphere class that allows users to do this. The method should calculate the radius from the passed circumference, and then use that radius to make an instance of Sphere.

I've provided some tests for you to check your modified class.

In [11]:
# Your answer here.
In [12]:
assert Sphere.from_circ(0).radius == 0, "Test 1 failed."
assert Sphere.from_circ(3 * math.pi).radius == 1.5, "Test 2 failed." 
assert math.isclose(Sphere.from_circ(6).radius, 0.95, abs_tol=0.1), "Test 3 failed."
assert math.isclose(Sphere.from_circ(6).volume(), 3.65, abs_tol=0.1), "Test 4 failed."
assert Sphere.from_circ(6).__str__() == "A Sphere with volume 3.65", "Test 5 failed."
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-12-c64f2af0ae75> in <module>
----> 1 assert Sphere.from_circ(0).radius == 0, "Test 1 failed."
      2 assert Sphere.from_circ(3 * math.pi).radius == 1.5, "Test 2 failed."
      3 assert math.isclose(Sphere.from_circ(6).radius, 0.95, abs_tol=0.1), "Test 3 failed."
      4 assert math.isclose(Sphere.from_circ(6).volume(), 3.65, abs_tol=0.1), "Test 4 failed."
      5 assert Sphere.from_circ(6).__str__() == "A Sphere with volume 3.65", "Test 5 failed."

NameError: name 'Sphere' is not defined
In [ ]:
!jupyter nbconvert _03-py-tests-classes-practice.ipynb --to html --template classic --output 03-py-tests-classes-practice.html

Have fun!