Skip to content

Commit 8dd76f1

Browse files
committed
📝 Update test section
* Switch to uv * Switch form items to cusy.tasks * Add hypothesis extensions * Update mocking, hypothesis and glossary * Add Test Driven Development
1 parent b44456b commit 8dd76f1

28 files changed

Lines changed: 1216 additions & 947 deletions

docs/appendix/checks.rst

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ Checks
112112
* ``1`` → True
113113
* ``0`` → False
114114
* ``-1`` → True
115-
* ``[0]`` → True (List with one item)
115+
* ``[0]`` → True (List with one task)
116116
* ``1 and 0`` → False
117117
* ``1 > 0 or []`` → True
118118

@@ -858,18 +858,19 @@ Checks
858858
├── README.rst
859859
├── pyproject.toml
860860
└── src
861-
   └── items
862-
   ├── __init__.py
863-
   ├── api.py
864-
   ├── cli.py
865-
   └── db.py
861+
   └── cusy
862+
   └── tasks
863+
   ├── __init__.py
864+
   ├── api.py
865+
   ├── cli.py
866+
   └── db.py
866867
867868
* Think about how you want to fulfil the above tasks. Which libraries and
868869
modules can you think of that could fulfil this task? Sketch the code for the
869870
modules of the Python API, the command line interface and the database
870871
connection.
871872

872-
I would create a :class:`DB` class in :file:`src/items/db.py` for
873+
I would create a :class:`DB` class in :file:`src/cusy/tasks/db.py` for
873874
communication with the database, in the following example for `tinydb
874875
<https://tinydb.readthedocs.io/en/latest/>`_:
875876

@@ -884,80 +885,80 @@ Checks
884885
db_path / f"{db_file_prefix}.json", create_dirs=True
885886
)
886887
887-
def create(self, item: dict):
888-
"""Create an item
888+
def create(self, task: dict):
889+
"""Create a task
889890
890891
Returns:
891-
id: The items id.
892+
id: The tasks id.
892893
"""
893894
894895
return id
895896
896897
def read(self, id: int):
897-
"""Reads an item.
898+
"""Reads a task.
898899
899900
Args:
900-
id (int): The item id of an item.
901+
id (int): The task id of an task.
901902
Returns:
902-
item: The item object."""
903-
return item
903+
task: The task object."""
904+
return task
904905
905906
def update(self, id: int, mods):
906-
"""Update an item in the database.
907+
"""Update a task in the database.
907908
908909
Args:
909-
id (int): The item id of an item.
910-
mods (Item): The modifications to be made to this item.
910+
id (int): The task id of a task.
911+
mods (Task): The modifications to be made to this task.
911912
"""
912913
self._db.update(changes, doc_ids=[id])
913914
914915
def delete(self, id: int):
915-
"""Deletes an item in the database.
916+
"""Deletes a task in the database.
916917
917918
Args:
918-
id (int): The item id of an item.
919+
id (int): The task id of a task.
919920
"""
920921
self._db.remove(doc_ids=[id])
921922
922923
def close(self):
923924
"""Closes the database connection."""
924925
self._db.close()
925926
926-
Then I would use :func:`dataclass` in :file:`src/items/api` to create an
927-
:class:`Item` class:
927+
Then I would use :func:`dataclass` in :file:`src/cusy/tasks/api` to create an
928+
:class:`Task` class:
928929

929930
.. code-block:: python
930931
931932
from dataclasses import dataclass, field
932933
933934
934935
@dataclass
935-
class Item:
936+
class Task:
936937
summary: str = None
937938
owner: str = None
938939
state: str = "todo"
939940
id: int = field(default=None, compare=False)
940941
941942
942-
class ItemsException(Exception):
943+
class TasksException(Exception):
943944
pass
944945
945946
946-
class ItemsDB:
947+
class TasksDB:
947948
def __init__(self, db_path):
948949
self._db_path = db_path
949-
self._db = DB(db_path, ".items_db")
950+
self._db = DB(db_path, ".tasks_db")
950951
951-
def add_item(self, item: Item):
952+
def add_task(self, task: Task):
952953
return
953954
954-
def get_item(self, item: Item):
955+
def get_task(self, task: Task):
955956
return
956957
957-
def update_item(self, item: Item):
958+
def update_task(self, task: Task):
958959
return
959960
960-
def delete_item(self, item: Item):
961+
def delete_task(self, task: Task):
961962
return
962963
963964
def close(self):
@@ -966,16 +967,16 @@ Checks
966967
def path(self):
967968
return self._db_path
968969
969-
:class:`ItemsException` Item and :class:`ItemsDB` are then provided in
970-
:file:`src/items/__init__.py`:
970+
:class:`TasksException` Task and :class:`TasksDB` are then provided in
971+
:file:`src/cusy/tasks/__init__.py`:
971972

972973
.. code-block:: python
973974
974-
from .api import ItemsException, Item, ItemsDB
975+
from .api import TasksException, Task, TasksDB
975976
976977
.. seealso::
977-
You can find a complete example at `github.com/veit/items
978-
<https://github.com/veit/items/>`_.
978+
You can find a complete example at `github.com/veit/cusy.tasks
979+
<https://github.com/veit/cusy.tasks/>`_.
979980

980981
:doc:`/save-data/files-directories`
981982
-----------------------------------

docs/appendix/glossary.rst

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Glossary
88
User Acceptance Test
99
Verification that software functions as intended from the user’s
1010
perspective and that users accept the software. Acceptance tests are
11-
primarily used in :term:` Extreme Programming`.
11+
primarily used in :term:`Extreme Programming`.
1212

1313
Argument
1414
A value that is passed to a function. There are two types of arguments:
@@ -630,8 +630,11 @@ Glossary
630630
* :ref:`wheels`
631631

632632
whey
633-
Simple Python :term:`wheel` builder with automation options for
634-
:term:`trove-classifiers`.
633+
A simulated version of a class or method. It has a functional
634+
implementation but uses a kind of shortcut, making it unsuitable for
635+
production use. For example, an in-memory database might be used for
636+
testing purposes but not in production. In this context, the in-memory
637+
database would act as a fake.
635638

636639
.. _end-packaging:
637640

@@ -668,7 +671,7 @@ Glossary
668671
:doc:`/test/unittest`
669672
supports you in the automation of tests.
670673
:doc:`/test/mock`
671-
allows you to create and use mock objects.
674+
allows you to create and use :term:`Mock` objects.
672675
:doc:`../document/doctest`
673676
allows you to test tests written in Python :term:`docstrings
674677
<Docstring>`.
@@ -689,23 +692,41 @@ Glossary
689692
platforms.
690693

691694
Dummy
692-
Object that is passed around but never actually used. Normally dummies
693-
are only used to fill :term:`parameter` lists.
695+
An object that is passed along but is not intended for use in your tests.
696+
It has no effect whatsoever on the behaviour of your tests. An example of
697+
a dummy could be an attribute that is required to instantiate a class but
698+
is not needed for the test.
694699

695700
``except``
696701
Keyword used to intercept an :term:`exception` and handle it carefully.
697702

698703
Fake
699-
Object that has an implementation that actually works, but usually takes
700-
a shortcut that makes it unsuitable for production.
704+
A simulated version of a class or method. It has a functional
705+
implementation but uses a kind of shortcut, making it unsuitable for
706+
production use. For example, an in-memory database might be used for
707+
testing purposes but not in production. In this context, the in-memory
708+
database would act as a fake.
701709

702710
Integration test
703711
Tests that check whether the different parts of the software work
704712
together as expected.
705713

706714
Mock
707-
Objects programmed with :term:`exceptions <exception>` that form a
708-
specification of the calls you are likely to receive.
715+
A mock simulates attributes, classes and methods. This enables developers
716+
to
717+
718+
719+
* test certain aspects of their code more effectively
720+
* test in a controlled environment
721+
* test for external dependencies
722+
723+
Unlike :term:`stubs <Stub>`, a mock can be used not only to verify the
724+
result, but also to check **how** the result was achieved or whether the
725+
correct methods were called.
726+
727+
The Python library for mocks is :doc:`unittest.mock <../test/mock>`. It
728+
is also supported by :doc:`../test/pytest/index`. Alternatively, however,
729+
you can also use `pytest-mock <https://pypi.org/project/pytest-mock/>`_.
709730

710731
.. seealso::
711732
* `Mock object <https://en.wikipedia.org/wiki/Mock_object>`_
@@ -720,30 +741,33 @@ Glossary
720741
Tests to protect against new errors or regressions that may occur as a
721742
result of new software and updates.
722743

723-
Stubs
724-
provide ready-made responses to calls made during the test and usually
725-
do not react at all to anything that has not been programmed for the
726-
test.
744+
Spy
745+
Spies are used to wrap real objects and, by default, forward all method
746+
calls to the original object. They therefore intercept all calls to a
747+
real object and log them. This makes it possible to monitor calls to the
748+
original object (for example, how often a particular method has been
749+
called) without replacing the original object (as a :term:`mock` would
750+
do, for instance).
751+
752+
Stub
753+
A non-real object with pre-programmed behaviour. In most cases, stubs
754+
simply return fixed values, known as *canned data*.
727755

728756
Test-driven development
729757
TDD
730-
A technique for creating software that guides software development by
758+
A software development technique that guides the development process by
731759
writing tests. It was developed in the late 1990s by Kent Beck as part of
732-
Extreme Programming. Essentially, it involves repeating three simple
733-
steps:
760+
:term:`Extreme Programming`. Essentially, it involves repeating three
761+
simple steps:
734762

735763
* Write a test for the next feature to be added.
736764
* Write the function code until the test passes.
737-
* Refactor both the new and old code to make it well structured.
765+
* Revise both the new and the old code to ensure it is well structured.
738766

739-
Although these three steps, often summarised as *‘red – green –
740-
refactor’*, form the core of the process, there is also an important
741-
first step, in which a list of test cases is created. One of these tests
742-
is then selected, *‘Red – Green – Refactor’* is applied to it, and the
743-
next test is selected. During the process, further tests are added to
744-
this list.
767+
These three steps are often summarised as *‘Red – Green – Refactor’*.
745768

746769
.. seealso::
770+
* :doc:`../test/tdd`
747771
* `Canon TDD <https://tidyfirst.substack.com/p/canon-tdd>`_ by Kent
748772
Beck
749773
* `Test-driven development by example

docs/document/sphinx/main.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
app = FastAPI()
77

88

9-
class Item(BaseModel):
9+
class Task(BaseModel):
1010
name: str
1111
price: float
1212
is_offer: Optional[bool] = None
@@ -17,11 +17,11 @@ def read_root():
1717
return {"Hello": "World"}
1818

1919

20-
@app.get("/items/{item_id}")
21-
def read_item(item_id: int, q: Optional[str] = None):
22-
return {"item_id": item_id, "q": q}
20+
@app.get("/cusy.tasks/{task_id}")
21+
def read_task(task_id: int, q: Optional[str] = None):
22+
return {"task_id": task_id, "q": q}
2323

2424

25-
@app.put("/items/{item_id}")
26-
def update_item(item_id: int, item: Item):
27-
return {"item_name": item.name, "item_id": item_id}
25+
@app.put("/cusy.tasks/{task_id}")
26+
def update_task(task_id: int, task: Task):
27+
return {"task_name": task.name, "task_id": task_id}

docs/document/sphinx/main.py.orig

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
from typing import Optional
2-
32
from fastapi import FastAPI
43

54
app = FastAPI()
65

7-
86
@app.get("/")
97
def read_root():
108
return {"Hello": "World"}
119

12-
13-
@app.get("/items/{item_id}")
14-
def read_item(item_id: int, q: Optional[str] = None):
15-
return {"item_id": item_id, "q": q}
10+
@app.get("/cusy.tasks/{task_id}")
11+
def read_task(task_id: int, q: Optional[str] = None):
12+
return {"task_id": task_id, "q": q}

docs/oop/dataclasses.rst

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ create such a class.
1212
matrices with numbers I use :doc:`Numpy
1313
<Python4DataScience:workspace/numpy/index>`.
1414

15-
Let’s say we want to store a class that represents an item with ``summary``,
15+
Let’s say we want to store a class that represents a task with ``summary``,
1616
``owner``, ``state`` and ``id``. We can define such a class with:
1717

1818
.. code-block:: pycon
1919
2020
>>> from dataclasses import dataclass
2121
>>> @dataclass
22-
... class Item:
22+
... class Task:
2323
... summary: str = None
2424
... owner: str = None
2525
... state: str = "todo"
@@ -32,30 +32,30 @@ If I display the instance of the class, I get the class name and the attributes:
3232
.. code-block:: pycon
3333
3434
>>> i1
35-
Item(summary='My first item', owner='veit', state='todo', id=1)
35+
Task(summary='My first task', owner='veit', state='todo', id=1)
3636
3737
In general, data classes are used as syntactic sugar for creating classes that
3838
store data. You can add extra functionality to your classes by defining methods.
39-
We will add a method to the class that creates an Item object from a
39+
We will add a method to the class that creates a Task object from a
4040
:doc:`Dict <../types/dicts>`:
4141

4242
.. code-block:: pycon
4343
4444
>>> @dataclass
45-
... class Item:
45+
... class Task:
4646
... ...
4747
... @classmethod
4848
... def from_dict(cls, d):
49-
... return Item(**d)
49+
... return Task(**d)
5050
...
51-
>>> item_dict = {
52-
... "summary": "My first item",
51+
>>> task_dict = {
52+
... "summary": "My first task",
5353
... "owner": "veit",
5454
... "state": "todo",
5555
... "id": 1,
5656
... }
57-
>>> Item.from_dict(item_dict)
58-
Item(summary='My first item', owner='veit', state='todo', id=1)
57+
>>> Task.from_dict(task_dict)
58+
Task(summary='My first task', owner='veit', state='todo', id=1)
5959
6060
.. tip::
6161
`cusy seminar: Advanced Python

0 commit comments

Comments
 (0)