diff --git a/src/python_package/__init__.py b/src/python_package/__init__.py index 851aa6a..c0a0890 100644 --- a/src/python_package/__init__.py +++ b/src/python_package/__init__.py @@ -4,8 +4,8 @@ __version__ = metadata.version("python_package") -from .mockup import hello_world +from .mockup import hello_world, saved_world # The __all__ variable is a list of variables which are imported # when a user does "from example import *" -__all__ = ["hello_world"] +__all__ = ["hello_world", "saved_world"] diff --git a/src/python_package/mockup.py b/src/python_package/mockup.py index d8232c6..4b39c12 100644 --- a/src/python_package/mockup.py +++ b/src/python_package/mockup.py @@ -13,5 +13,33 @@ def hello_world(n: int) -> str: ------- str str of 'hello world' n-times + + Examples + -------- + >>> hello_world(3) + 'hello world hello world hello world' """ return " ".join(repeat("hello world", n)) + + +def saved_world(filename: str) -> int: + """ + Count how many times 'hello world' is in a file. + + Parameters + ---------- + filename : str + The file to read + + Returns + ------- + int + How many times 'hello world' is in the file + + Examples + -------- + >>> saved_world("not-real.txt") # doctest: +SKIP + """ + with open(filename, "r") as f: + content = f.read() + return content.count("hello world") diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..3207159 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,112 @@ +--- +marp: true +theme: uncover +paginate: true +backgroundColor: #fff +--- + +# **Getting started with pytesting** + +--- + +## Quick start + +### Installation + +Install pytest. e.g., from the "dev" dependencies + +```bash +pip install ".[dev]" +``` + +### How to use + +To execute the tests run e.g. + +```bash +pytest +``` + +--- + +## Test development tips + +--- + +### Folder and test naming + +1. The tests for functions in `.py` should go in `tests/test_.py` + + e.g., the tests for [python_package/mockup.py](../src/python_package/mockup.py) are in [tests/test_mockup.py](test_mockup.py) + +2. The test names should start with `def test_ ...` + + e.g., `def test_hello_world(): ...` + +--- + +### Some Pytest decorators + +1. To indicate that the test function is expected to fail you can prepend + + ```python + @pytest.mark.xfail(raises=TypeError) + def test_hello_world_str(): ... + ``` + +--- + +2. To setup and cleanup any resources for a test you can use [pytest fixtures with `yield`](https://dev.to/dawidbeno/understanding-yield-in-pytest-fixtures-4m38) + + ```python + @pytest.fixture + def temp_file(): + # set up + < code to create a file> + # return + yield + the_file + # clean up + < code to remove the file> + ``` + +--- + +### Doctests + +You can also include tests in your docstrings using `>>>` followed by the expected result e.g. + +```python +def hello_world(n): + """ + Prints 'hello world' n-times. + ... + + Examples + -------- + >>> hello_world(3) + 'hello world hello world hello world' + ... + """ +``` + +Needs `addopts = --doctest-modules` in `pytest.ini` in root of directory + +--- + +### Skipping in doctests + +If you know that the test cannot succeed but would like to include an example usage in the docstring still then you can add `# doctest: +SKIP` e.g. + +```python +def saved_world(filename): + """ + Count how many times 'hello world' is in a file. + ... + + Examples + -------- + >>> saved_world("not-real.txt") # doctest: +SKIP + ... + """ +``` diff --git a/tests/test_mockup.py b/tests/test_mockup.py index 80ebb89..9307ed3 100644 --- a/tests/test_mockup.py +++ b/tests/test_mockup.py @@ -1,7 +1,31 @@ -from python_package import hello_world +from python_package import hello_world, saved_world +import pytest def test_hello_world_3times(): expected = "hello world hello world hello world" result = hello_world(3) assert result == expected + + +@pytest.mark.xfail(raises=TypeError) +def test_hello_world_str(): + hello_world("3") + + +@pytest.fixture +def temp_file(): + # set up + filename = "temp_hellooo.txt" + with open(filename, "w") as f: + f.write("hello world hello world hello world") + yield filename + # clean up + import os + + os.remove(filename) + + +def test_saved_world_3times(temp_file): + result = saved_world(temp_file) + assert result == 3