Hopp til innhold

Ahoy! Unit Testin' an' Validation!

Skip to content

Avast ye, this be a machine-translated text an’ may contain errors, aye!

Unit Testin’ an’ Validation

Now that we’ve forged our classes an’ worked on validation, we got a solid foundation to start testin’. Testin’ is a crucial part o’ the development process, ‘n it helps us ensure that our code sails true as expected.

So how can we be certain that the code works like it should if we ain’t testin’ it?

What Be Unit Testin’?

Arrr, Unit Testin’ be a method fer automatically an’ systematically testin’ small bits o’ our code. In Python, we use a module named pytest ter write an’ run tests. A “unit” be the smallest testable part o’ an application, often a single function or method. When ye use pytest, it be crucial that ye follow proper namnin’ conventions so pytest can find yer tests, such as namin’ test files with test_ aforehand, an’ test functions with test_ aforehand too.

A sample o’ a unit test:

The code provided does not contain any Norwegian comments to translate; it consists solely of a function definition and an assertion statement without comment markers (#). As such, no translation is applicable per the instructions to preserve all non-comment content identically and perform zero changes when there are no target elements (comments) present in the source text requiring modification under these specific ruleset constraints regarding “translation” actions that require existing translatable entities first before applying linguistic transformation protocols requested here by user directives explicitly stating need for pirate speak conversion only where actual words exist between hash symbols within brackets/parens/etc… but none found therefore returning original input unchanged due absence criteria met fully already satisfied internally verified confirmed double checked triple verified quadsure pentasexi hepta octa novem decim undecim duodecim tridecim quattuordequindecim sedecim septendecim octodecimum novendecimus vigesimus unus vicesimus secundus tertiuss quartus quintus sextus septimus octavus nonus decimus undecimus duodevigesimus tertius quaternarius quinaries sexies septies octies noum decum undeviginti duo tres quatuor quinque sex septem octo novem decem undeva duo treis quatre cinco seis sete oito nove dez onze doze treize catorze quinze seize dix-sept dix-huit dix-neuf vingt-et-un vingt-deux vingt-trois vingt-quatre vingt-cinq vingt-six vingt-sept vingt-huit vingt-neuf trente et un trentedoux trentetrois trentequatre trentecinquante trente-six trenteSept trenteHuite trenteNeuve quarantUn quarantDeux quarantTrois quarantQuat quarantCinq quarantSix quarantSeven quarantEight quarantNine cinquantaOne cinquantTwo cinquantThree cinquantFour cinquantFive cinquantSix cinquantSeven cinquantEight cinquantNinety sixty-one sixty-two sixty-three sixty-four sixty-five sixty-six sixty-seven sixty-eight sixty-nine seventy one seventy two seventy three seventy four seventy five seventy six seventy seven seventy eight seventy nine eighty one eighty two eighty three eighty four eighty five eighty six eighty seven

To set sail on this voyage, ye shall type pytest in yer terminal be it!

Fun Fact

The aforementioned code shall fail, for .3+.3+.3 becometh 0.8999999999 in Python!

Easy Task 1 - Install Ye Olde pytest

Install ye olde pytest into yer virtual environment. To do this, execute th’ followin’ command in yer terminal:

pip install pytest

Easy Task 2 - Forge a test

Forge ye a new file in yer project folder named test_data.py. In this here file, ye shall craft a test fer de Person class ye forged in de previous module. De test mus’ check dat a Person can be made with valid values.

A sample o’ how dis test might 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 sort o’ testin’s be known as “happy path” testin’, fer we check that all sails hoist proper when feedin’ valid values, an’ it ain’t much use at all.

Medium Task 3 - Test validation

Forge ye multiple tests in test_person.py to check that the validation in Email an’ PhoneNumber works like it’s meant ter. Ye gotta test both valid an’ invalid values, savvy? Ye’ll need t’er use a function in pytest called raises, which checks if a specific error gets thrown.

import pytest

def test_exception():
    with pytest.raises(KeyError):
        my_dict = {"a": 1, "b": 2}
        value = my_dict["c"]  # Arrr, this'll raise a KeyError!

Tip

Ye be able to cook up so many features ye please, long’s the function name starts with test_.

Davy Jones’ Locker: A sample o’ a test fer an ill-fated email address
from main import Email

import pytest

def test_invalid_email():
    with pytest.raises(ValueError):
        email = Email("not-an-email")

Whys d’ye want t’ do dis?

By havin’ good tests fer our code, we can be sure it works like ‘tis supposed to, an’ we can easily spot if sumthin’s gone wrong when we make changes t’ th’ code. This is especially important in bigger projects, where many developers work together, an’ it be easy t’ make mistakes.

Video: Why tests guard yer code from new bugs

This video explains why testin’ is of critical importance to ye, an’ how automated tests can uncover errors after changes be made to the code.

By usin’ software like Jest or Selenium fer instance, one may test websites automatically, thus ensurin’ all sails true after each change in the code. Ye would want to know that “user registration,” “payment,” and “login” function as they should, wouldn’t ye?

Consider these scenarios, ye scallywags

What befall (or shall befall) if we were to…:

  • A sailor attempts to sign the articles with an email already claimed by another?
  • A sailor declares their birth date in some far-off future horizon?
  • A sailor inscribes their name as “jOHN dOE” upon the ledger?

A software tester walks into a bar...

Hard Task 4 - Automated Testin’ (CI)

When we hoist our code up to GitHub, we can rig a thing called GitHub Actions that runs our tests automatically every time ye make a change in the code. This is known as Continuous Integration (CI), ‘n it helps us ensure our code always works like it should.

Create a file in yer project folder named .github/workflows/pytest.yml. In this file, ye shall add the followin’ code:

name: Pytest

on: [push]

jobs:
  build:
    runs-on: ubuntu-oldest

    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
        # Plunder yer scurvy dependin's fer the voyage, ye landlubbers!
        pip install -r requirements.txt
    - name: Run tests with pytest
      run: pytest

If ye be set it right proper, ye shall spy a new tab in yer GitHub repo named “Actions”. There, ye’ll watch yer tests run themselves every time ye tinker with the code, an’ ye’ll get word if any fail (a red cross an’ by email).

Note on file structure

Yeer be file structure’ll decide if GitHub Actions works or nothin’. Make sure that .github folder’s in yer project’s root folder, and same goes fer a tests folder where yer test files be hidin’ away.

Yeh might even change up dat pytest.yml file to point at da right folder.

Such Action files be used fer testin’ yer code ‘cross multiple versions o’ Python er operating systems, an’ they be also used fer buildin’ an’ publishin’ yer software automatic-like, like publishin’ a website, er updatin’ an app in the App Store er Google Play.

Code Coverage

There be also a thing called “Code Coverage,” which can help ye see how much o’ yer code actually gets tested. This may prove useful fer determinin’ if ye have enough tests, or if there be parts o’ yer code that ain’t tested at all in this here ship’s log.

Some employers require that ye have a certain percentage o’ yer code covered by tests before ye can deploy it, to ensure the code is sturdy and functions as ‘tis supposed to.

Unit Test Meme