Test-Driven Development with PyTest - Part 2
Tags: python, testing, beginners, opensource
Published: 2019-12-08 17:17:29

# Introduction
Welcome back to part 2 of the test-driven development with PyTest. 

For part two of the TDD with Pytest. 

I would be covering the [project structure](https://docs.python-guide.org/writing/structure/#test-suite) where your test cases will reside. 

The creation of test cases as a **class** or **function**  under pytest. 

Do head to [part 1](https://www.maxongzb.com/test-driven-development-with-pytest-part-1-read-time-4-mins/) of the series before proceeding with part 2.

It is assumed that a Linux system is used for this series. 

Please use a Linux variant like [Ubuntu](https://ubuntu.com/) or a cloud IDE like [Codenvy](https://codenvy.com/) running a Linux virtual container.

#  Project Structure
Whenever you are creating test cases for your Python program. 

There are the various school of thoughts on this for the location you will be putting your test cases in.

Your test cases could **either** reside within the program's subdirectories you are testing or create a **centralized** directory which your test cases reside in. 

Some pros and cons come with it. It is a matter of project preference that is set by the development & QA team.

For me, I would prefer a **centralized** location for my test cases under ** tests** directory. 

As it **reduces** the amount of time that a developer has to **hunt** and **test** out the various program components.

If you had read [Python Testing with Pytest by Brian Okken](https://www.amazon.com/Python-Testing-pytest-Effective-Scalable/dp/1680502409). 

He did a combination of both for **bite-sized** consumption in each chapter and putting the test cases under specific **test** directory.

Therefore going through his book will allow you to consider which will be a better choice for you.

You could access the book's [source code](https://pragprog.com/titles/bopytest/source_code) and use the book as a reference guide to help you learn about **Pytest** in Python.

#  Create Test Cases as Class or Function
When you are just starting to learn to create test cases using Pytest. 

We start by **embedding** a test case **function** to the program:

## Embedded Test Case as Function
Create a file called `tutorial.py` and with a `test_input` function:

```bash
touch tutorial.py
```

**tutorial.py**
'''python
a = 0
b = 0


def test_input():
    assert a >= 0
    assert b >= 0

'''
Now run the following command in your own terminal.

```bash
pytest tutorial.py
```
![pytest 1](//images.ctfassets.net/ly2f59p4unnn/2RCtUJpro21quC0DncB7yt/8dc60b502a859cc9cc4d9153b1ba69c9/pytest_1.png)

This is an example of creating your test cases as a **function**. 

When a file has more than a certain amount of lines. It has to be separated for ease of maintainability.

## Test cases in a Separate File
Now refactor this test case into a separate file called `test_tutorial.py` which was created during part 1 of the series:

**test_tutorial.py**
```python
import tutorial


def test_input():
    a = tutorial.a
    b = tutorial.b

    assert a >= 0
    assert b >= 0

``` 
Now execute the previous command. 

```bash
pytest test_tutorial.py
```

![pytest 2](//images.ctfassets.net/ly2f59p4unnn/3AZiX052UtzTB3WRPizDuz/c8ffa2af6561419457c984694cc7f3a2/pytest_2.png)

Besides importing the `tutorial.py` and adding variable declaration as part of the function.

## Test Cases in a Class
Classes are commonly used as a way to store **multiple test cases** under a specific feature or part of the program. 

The reason for it is to allow testing of a **specific** part of the program.  

Without another feature to **interfere** with your test cases that might lead to an **inaccurate** test result. 

Now, let us refactor the previous function test case into a class function called `TestInput`:

```python
import tutorial


class TestInput:
    def test_a_input(self):
        a = tutorial.a

        assert type(a) != float
        assert a >= 0

    def test_b_input(self):
        b = tutorial.b

        assert type(b) != float
        assert b >= 0

```

Now let us run the pytest command with the `-v (verbose)` option enabled on the newly refactored class test case.

```bash
pytest -v test_tutorial.py
```

![pytest 3](//images.ctfassets.net/ly2f59p4unnn/40r3NFAOsulzTMo0hCcVqE/3e30630d907a4dc1fd266eccba844476/pytest_3.png)

You should have two test cases which are `test_a_input` & `test_b_input` under `TestInput`. 

Now for this `TestInput` class is used to test the input parameters of the program called `tutorial.py`.

We could create and test the function in the `tutorial.py` called `getsum` which calculates the result of parameter `a` and `b` as another class:

**tutorial.py**
```python
a = 0
b = 0


def getsum(a, b):
    return a + b

```

**test_tutorial.py**
```python
import tutorial


class TestInput:
    def test_a_input(self):
        a = tutorial.a

        assert type(a) != float
        assert a >= 0

    def test_b_input(self):
        b = tutorial.b

        assert type(b) != float
        assert b >= 0


class TestGetSum:
    def test_addition(self):
        assert tutorial.getsum(2, 8) == 10

```

Now let us run the following command, which results with **3** test cases under **2** classes.

```bash
pytest -v test_tutorial.py
```

![pytest 4](//images.ctfassets.net/ly2f59p4unnn/hsCB8hAzaOQnPPfdpk5Hb/47c9fc9577c52ceb939e7212957fb262/pytest_4.png)

Imagine you could enhance it further by creating multiple test cases within each class. That is the reason why having a test case as a class is useful for **maintainability** purposes and **separation of concern**.

# Conclusion
I hope this article provides you with an understanding of how your test cases should reside for the project structure.

Along with the understanding of the difference creating test cases as a **function** or **class** for [PyTest](https://docs.pytest.org/en/latest/).

Do note that the next part of the series, I will show my attempt to use a PyTest for a [code kata](http://codekata.com/) exercise to demonstrate your understanding of PyTest.


If you like my article, please **sign up** for Max [Adventurer's Newsletter](https://maxongzb.activehosted.com/f/1) for awesome content I stumble across weekly in **Python**, **Startup** and **Web Development**.

You can also **follow** me to get the **latest** update of my article on **Dev**

This post was originally posted on Max's blog at [Test-Driven Development With PyTest - Part 2: Reading Time 4 Mins](https://www.maxongzb.com/test-driven-development-with-pytest-part-2-reading-time-4-mins/) and [Photo by Joyce McCown on Unsplash](https://unsplash.com/photos/gpiKdZmDQig)

# References
* [Ubuntu](https://ubuntu.com/)
* [Codenvy](https://codenvy.com/)
* [Python Testing with Pytest by Brian Okken](https://www.amazon.com/Python-Testing-pytest-Effective-Scalable/dp/1680502409)
* [Source code for Python Testing with Pytest book](https://pragprog.com/titles/bopytest/source_code)
* [Code Kata](http://codekata.com/)
* [Structuring Your Project](https://docs.python-guide.org/writing/structure/#test-suite)