This is a machine-translated text that may contain errors!
Now that we have created classes and worked with validation, we have a good basis for starting with testing. Testing is an important part of the development process, and it helps us ensure that our code works as expected.
Because how can we be sure that the code works as it should, if we don’t test it?
What is Unit Testing?
Unit Testing is a method for automatically and systematically testing small parts of our code. In Python, we use a module called pytest to write and run tests. A “unit” is the smallest testable part of an application, often a single function or method. When using pytest, it is important that we follow the correct naming conventions for pytest to be able to find our tests, such as naming test files with test_ in front, and test functions with test_ in front.
Example of a unit test:
def test_add():
assert 0.3 + 0.3 + 0.3 == 0.9 # Tester om addisjon fungerer som forventet.
Run by typing pytest in the terminal.
Fun fact
The code above will fail, because .3+.3+.3 becomes 0.8999999999 in Python!
Task 1 - Install pytest
Install pytest in your virtual environment. You do this by running the following command in the terminal:
pip install pytest
# Installerer pytest for testing
Task 2 - Create a test
Create a new file in your project folder called test_data.py. In this file, you should create a test for the Person class you created in the previous module. The test should check that a Person can be created with valid values.
Example of how the test can look:
from main import Person
def test_person_working():
bob_kaare = Person(name="Bob Kåre",
eye_color=EyeColor.BLUE,
phone_number=PhoneNumber("12345678"),
email=Email("bob_kaare@example.com"))
assert bob_kaare.name == "Bob Kåre"
assert bob_kaare.eye_color == EyeColor.BLUE
This type of testing is called “happy path” testing, because we are testing that everything works as it should when we provide valid values, and is not particularly useful.
Task 3 - Test Validation
Create more tests in test_person.py that check that the validation in Email and PhoneNumber works as expected. Test both valid and invalid values. Here you must use a function in pytest called raises, which checks that a specific error is raised.
import pytest
def test_exception():
with pytest.raises(KeyError):
my_dict = {"a": 1, "b": 2}
value = my_dict["c"] # This will "raise" a KeyError
Tip
You can create as many functions as you like, as long as the function name starts with test_.
Solution: Example of a test for invalid email
from main import Email
import pytest
def test_invalid_email():
with pytest.raises(ValueError):
email = Email("not-an-email")
Why do this?
By having good tests for our code, we can be sure that it works as it should, and we can easily find out if something has gone wrong if we make changes to the code. This is especially important in larger projects, where many developers work together, and it is easy to make mistakes.
By, for example, using software like Jest or Selenium, one can test websites automatically, and thus ensure that everything works as it should after each change in the code. We would like to know that “user registration”, “payment” and “login” work as they should, right?
Consider the following scenarios
What happens (or should happen) if..:
- A user tries to register with an email that is already in use?
- A user has a future date of birth?
- A user enters the name “jOHN dOE”?
Task 4 - Automatic Testing (CI)
When we publish code on GitHub, we can set up something called GitHub Actions that can run our tests automatically every time we make a change to the code. This is called Continuous Integration (CI), and it helps us ensure that our code always works as expected.
Create a file in your project folder called .github/workflows/pytest.yml. In this file, you should add the following code:
name: Pytest
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
# Installer eventuelle andre avhengigheter prosjektet ditt trenger
pip install -r requirements.txt
- name: Run tests with pytest
run: pytest
If you have added it correctly, you will now see a new tab in your GitHub repository called “Actions”. Here you will be able to see that your tests are run automatically every time you make a change to the code, and you will be notified if any of the tests fail (red cross and via email).
Note about file structure
Your file structure will determine whether GitHub Actions works or not. Make sure that the .github folder is located in the root of your project, and the same goes for a tests folder where your test files are located.
You can optionally modify the pytest.yml file to point to the correct folder.
Such Actions files can also be used to test your code on multiple versions of Python or operating systems, they can also be used to build and publish your software automatically, e.g. publish a website, or update an app in the App Store or Google Play.
Code Coverage
There is also something called “Code Coverage”, which can help you see how much of your code is actually being tested. This can be useful for finding out if you have enough tests, or if there are parts of your code that aren’t being tested at all.
Some workplaces require you to have a certain percentage of your code covered by tests before you can deploy it, to ensure that the code is robust and functions as it should.

