# Unit Tests & Classes¶

**Tomas Beuzen, September 2020**

These exercises complement Chapter 3.

```
import math
```

## Exercises¶

### 1.¶

The function `area()`

accepts the argument `radius`

and calculates the area of a circle. Write three tests using `assert`

statements for the following conditions:

Assert that

`area(1)`

returns a`float`

;Assert that

`area(0)`

returns a value of 0;Assert that

`area(5)`

is approximately equal to 78.5 (hint:`math.isclose(..., abs_tol=0.1)`

)

```
def area(radius):
"""Calculate the area of a circle based on the given radius."""
return math.pi * radius ** 2
```

```
# Your answer here.
```

### 2.¶

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:

```
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'
```

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

### 3.¶

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`

.

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

### 4.¶

For this exercise I want you to create a class called `Circle`

. It should have the following characteristics:

It should be initiated with the argument

`radius`

and store this as an instance attribute.Have a method

`area()`

which calculates the area of the circle.Have a method

`circumference()`

which calculates the circumference of the circle.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.

```
class Circle:
"""A circle with a radius r."""
pass # Remove this line and add your answer here.
```

```
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
```

### 5.¶

Now, let’s create a new class `sphere`

that inherits from the `circle`

class we created above. It should have the following characteristics:

It should be initiated exactly the same as

`Circle`

was, with the single argument`radius`

which is stored as an instance attribute.Have a method

`volume()`

which calculates the volume of the sphere (\(\frac{4}{3}{\pi}{r^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.

```
# Your answer here.
```

```
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
```

### 6.¶

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.

```
# Your answer here.
```

```
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
```

## Solutions¶

### 1.¶

The function `area()`

accepts the argument `radius`

and calculates the area of a circle. Write three tests using `assert`

statements for the following conditions:

Assert that

`area(1)`

returns a`float`

;Assert that

`area(0)`

returns a value of 0;Assert that

`area(5)`

is approximately equal to 78.5 (hint:`math.isclose(..., abs_tol=0.1)`

)

```
def area(radius):
"""Calculate the area of a circle based on the given radius."""
return math.pi * radius ** 2
```

```
assert isinstance(area(1), float), 'Test 1 failed!'
assert area(0) == 0, 'Test 2 failed!'
assert math.isclose(area(5), 78.5, abs_tol=0.1)
```

### 2.¶

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:

```
area('10')
```

```
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-28e1bc493b84> in <module>
----> 1 area('10')
<ipython-input-13-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'
```

```
def area(radius):
"""Calculate the area of a circle based on the given radius."""
try:
return math.pi * radius ** 2
except TypeError:
print(f"radius should be a number but you entered a {type(radius)}")
except:
print("Some other error occurred!")
```

### 3.¶

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`

.

```
def area(radius):
"""Calculate the area of a circle based on the given radius."""
if isinstance(radius, (int, float)):
return math.pi * radius ** 2
else:
raise TypeError(f"radius should be a number but you entered a {type(radius)}")
```

### 4.¶

For this exercise I want you to create a class called `circle`

. It should have the following characteristics:

It should be initiated with the argument

`radius`

and store this as an instance attribute.Have a method

`area()`

which calculates the area of the circle.Have a method

`circumference()`

which calculates the circumference of the circle.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.

```
class Circle:
"""A circle with a radius r."""
def __init__(self, radius):
self.radius = radius
def area(self):
"""Calculate the area of the circle."""
return math.pi * self.radius ** 2
def circumference(self):
"""Calculate the circumference of the circle."""
return 2.0 * math.pi * self.radius
def __str__(self):
return f"A Circle with radius {self.radius}"
```

```
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."
```

### 5.¶

Now, let’s create a new class `sphere`

that inherits from the `circle`

class we created above. It should have the following characteristics:

It should be initiated exactly the same as

`Circle`

was, with the single argument`radius`

which is stored as an instance attribute.Have a method

`volume()`

which calculates the volume of the sphere (\(\frac{4}{3}{\pi}{r^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.

```
class Sphere(Circle):
"""A sphere with a radius r."""
def volume(self):
"""Calculate the volume of the sphere."""
return 4 / 3 * math.pi * self.radius ** 3
def __str__(self):
return f"A Sphere with volume {self.volume():.2f}"
```

```
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."
```

### 6.¶

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.

```
class Sphere(Circle):
"""A sphere with a radius r."""
def volume(self):
"""Calculate the volume of the sphere."""
return 4 / 3 * math.pi * self.radius ** 3
@classmethod
def from_circ(cls, circumference):
"""Make an instance of Sphere from a circumference."""
radius = circumference / (2 * math.pi)
return cls(radius)
def __str__(self):
return f"A Sphere with volume {self.volume():.2f}"
```

```
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."
```