Testing
Testing your code is a hated but good practice. Repository ORM tries to make your testing experience less cumbersome.
You can use different strategies depending on the level of testing. For unit and
integration tests the FakeRepository
may be your best
option, for end-to-end ones, I'd use TinyDBRepository
.
Unit and integration tests¶
Unit tests are meant to test individual units of code, for example, a function or a method of a class. You'll probably use them to test your models or services.
import pytest
from repository_orm import Entity, FakeRepository, Repository
@pytest.fixture()
def repo() -> FakeRepository:
return FakeRepository()
class Author(Entity):
first_name: str
def create_greeting(repo: Repository, author_id: int) -> str:
author = repo.get(author_id, Author)
return f"Hi {author.first_name}!"
def test_greetings(repo: FakeRepository) -> None:
author = Author(id_=20, first_name="Brandon")
repo.add(author)
repo.commit()
result = create_greeting(repo, 20)
assert result == "Hi Brandon!" # noqa
End-to-end tests¶
End-to-end tests evaluate the whole functionality of the program from the eyes of the user. For example, testing a command line or the API endpoint. Usually the program loads the repository from storage at start time, which means that the FakeRepository can't be used.
We're going to create a
click command line
program called greet
that once it's called, it will return the first author in
the repository. It's a little bit more complex but bare with me.
from pathlib import Path
from typing import Generator
import click
import pytest
from click.testing import CliRunner
from repository_orm import Entity, Repository, TinyDBRepository, load_repository
# Model
class Author(Entity):
first_name: str
# Fixtures
@pytest.fixture(name="db_tinydb")
def db_tinydb_(tmp_path: Path) -> str:
tinydb_file_path = str(tmp_path / "tinydb.db")
return f"tinydb:///{tinydb_file_path}"
@pytest.fixture()
def repo(db_tinydb: str) -> Generator[TinyDBRepository, None, None]:
repo = TinyDBRepository(database_url=db_tinydb)
yield repo
repo.close()
# Service
def create_greeting(repo: Repository) -> str:
first_author = repo.all(Author)[0]
return f"Hi {first_author.first_name}, you're the first author!"
# Entrypoint
@click.command()
@click.argument("database_url")
def greet(database_url: str) -> None:
repo = load_repository(database_url)
print(create_greeting(repo))
repo.close()
# Test
def test_greetings(repo: TinyDBRepository, db_tinydb: str) -> None:
author = Author(id_=20, first_name="Brandon")
repo.add(author)
repo.commit()
runner = CliRunner()
result = runner.invoke(greet, [db_tinydb])
assert result.exit_code == 0
assert result.output == "Hi Brandon, you're the first author!\n" # noqa
First we define the fixtures, we start with db_tinydb
that uses the pytest's
tmp_path
fixture to create a random temporal directory and then sets the database url.
The repo
fixture uses that database url to create a TinyDBRepository
instance.
The model Author
and service create_greeting
are similar to the previous
section.
The entrypoint is where we define the command line interface, in this case the
command is going to be called greet
and it's going to accept an argument
called database_url
, it will initialize the repository and use the
create_greeting
to show the message to the user through the terminal.
To test this code, we first need to add an Author, so the function can look for
it. We do it in the first three lines of test_greetings
. Then we initialize
the
runner
which simulates a command line call, and we make sure that the program exited
well, and gave the output we expected.