Skip to content

Commit 332162b

Browse files
authoredNov 19, 2020
2020/11: improvements (#8)
* docs: add missing comments on Makefile * docs: fix diagram of setup/teardown flow for class tests * Improve examples A, B * Improve examples C (remove pymongo) * Improve examples D (remove pymongo) * Remove pymongo dependency references * Final global corrections, amends, improvements * Freeze versions in requirements.txt
1 parent 1f457ec commit 332162b

33 files changed

+936
-822
lines changed
 

‎Makefile

+5-4
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,20 @@ test-sequential: ## run sequential tests, ignoring on-purpose failing and parall
55
pytest -s -v examples/ \
66
--ignore=examples/A_helloworld/test_A2_failing.py \
77
--ignore=examples/F_parametrize/test_F2_parametrize_failing.py \
8-
--ignore=examples/H_parallelization
8+
--ignore=examples/H_parallelization \
9+
-k "not random_number_fixture_fails and not expect_zerodivisionerror_not_raised and not expect_zerodivisionerror_raised_other"
910

1011
test-parallel: ## run parallel tests
1112
pytest -s -v -n auto examples/H_parallelization
1213

13-
test-all:
14+
test-all: ## run all tests
1415
make test-sequential
1516
make test-parallel
1617

17-
install-requirements:
18+
install-requirements: ## pip install requirements
1819
pip install -r requirements.txt
1920

20-
run-jupyter:
21+
run-jupyter: ## run the jupyter notebook server
2122
jupyter notebook
2223

2324
help: ## show this help.

‎README.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@
44

55
## Requirements
66

7-
- Python >= 3.6
7+
- Python >= 3.6 (virtual env recommended)
88
- requirements listed in [requirements.txt](requirements.txt)
9-
- Tests C_fixtures, D_class_inherit require a running MongoDB server on localhost
109

1110
## Running tests
1211

1312
Run ALL tests with:
1413

1514
```bash
16-
pytest -s -v
15+
pytest -sv
1716
```
1817

19-
Run individual tests with:
18+
Run individual tests with (example):
2019

2120
```bash
22-
pytest -s -v examples/1_before_after
21+
# Only A1
22+
pytest -sv examples/A_helloworld/test_A1_helloworld.py
23+
24+
# All modules in B_before_after package
25+
pytest -sv examples/B_before_after
2326
```
+3-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
"""A1 - TEST with functions in module level, and testing exceptions
2-
"""
1+
"""A1 - TEST with functions in module level, and testing exceptions"""
32

43
import pytest
54

@@ -12,21 +11,12 @@ def test_divide():
1211
assert 4 / 2 == 2
1312

1413

15-
def test_divide_zero():
16-
"""This test will fail if the code inside "with pytest.raises(...)" does not raise the given exception
17-
"""
18-
with pytest.raises(ZeroDivisionError):
19-
assert 2 / 0 == 0
20-
21-
2214
@pytest.mark.skip("Don't want to run this right now!")
2315
def test_skip():
24-
"""This test is marked as "skip" and will not run (but shown on results)
25-
"""
16+
"""This test is marked as "skip" and will not run (but shown on results)"""
2617
assert 2 / 0 == 0
2718

2819

2920
def this_test_will_not_run():
30-
"""All test modules (filenames) and methods (function names) must start with "test_"
31-
"""
21+
"""All test modules (filenames) and methods (function names) must start with 'test_'"""
3222
assert True is False
+13-9
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
"""A2 - TEST with functions in module level, with a failing test
2-
"""
1+
"""A2 - TEST with functions in module level, with failing tests"""
32

4-
import pytest
53

4+
def test_failing_zerodivisionerror():
5+
2 / 0
66

7-
def test_failing():
7+
8+
def test_failing_compare_true_is_false():
9+
assert True == False
10+
11+
12+
def test_failing_compare_numbers_equality():
813
assert 2 == 3
914

1015

11-
def test_failing_not_raising():
12-
"""We expect this test to raise ZeroDivisionError, but will fail because it does not raise it (and the assert passes)
13-
"""
14-
with pytest.raises(ZeroDivisionError):
15-
assert 2 / 2 == 1
16+
def test_failing_compare_lists():
17+
l1 = ["A", "B", "C"]
18+
l2 = ["A", "C", "B"]
19+
assert l1 == l2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""A3 - TEST functions using pytest.raises"""
2+
3+
import pytest
4+
5+
6+
def test_expect_zerodivisionerror_raised(): # passes
7+
with pytest.raises(ZeroDivisionError):
8+
2 / 0
9+
10+
11+
def test_expect_zerodivisionerror_not_raised(): # fails
12+
with pytest.raises(ZeroDivisionError):
13+
2 / 1
14+
15+
16+
def test_expect_zerodivisionerror_raised_other(): # fails
17+
with pytest.raises(ZeroDivisionError):
18+
2 / "not a number"
19+
20+
21+
def test_expect_typeerror_raised(): # passes
22+
with pytest.raises(TypeError):
23+
2 / "not a number"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""TEST that will not run, because the filename does not start with 'test_'"""
2+
3+
4+
def test_what_will_not_run():
5+
assert True is False

‎examples/A_helloworld/this_test_wont_run_A3.py

-6
This file was deleted.

‎examples/B_before_after/test_B1_before_after_module.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
"""B1 - Test Before & After at Module level
22
Functions that run before & after all tests on the module, and before & after each test on the module.
3-
Run with: "pytest -v -s ..." to show print output
4-
"""
3+
Run with: "pytest -v -s ..." to show print output"""
54

65
my_variable = None
76

87

9-
def setup_module(module):
8+
def setup_module():
109
global my_variable
1110
print("A) Setup module. Should run BEFORE any test")
1211
my_variable = True
1312

1413

15-
def teardown_module(module):
14+
def teardown_module():
1615
print("D) Teardown module. Should run AFTER all tests")
1716

1817

19-
def setup_function(function):
18+
def setup_function():
2019
print("B) Setup function. Should run BEFORE each test")
2120

2221

23-
def teardown_function(function):
22+
def teardown_function():
2423
print("C) Teardown function. Should run AFTER each test")
2524

2625

‎examples/B_before_after/test_B2_before_after_class.py

+9-11
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
11
"""B2 - Test Before & After at Class level
22
Functions that run before & after all tests on the class, and before & after each test on the class.
3-
Run with: "pytest -v -s ..." to show print output
4-
"""
3+
Run with: "pytest -v -s ..." to show print output"""
54

65

7-
def setup_module(module):
6+
def setup_module():
87
print("A) Setup module. Should run BEFORE any test")
98

109

11-
def teardown_module(module):
10+
def teardown_module():
1211
print("F) Teardown module. Should run AFTER all tests")
1312

1413

15-
# noinspection PyMethodMayBeStatic,PyMethodParameters
1614
class TestBeforeAfterClass:
1715
my_variable: str
1816

1917
@classmethod
2018
def setup_class(cls):
21-
print("B) Setup module. Should run BEFORE any test of this class")
19+
print("B) Setup class. Should run BEFORE any of the tests in this class")
2220
cls.my_variable = "OK!"
2321

2422
@classmethod
2523
def teardown_class(cls):
26-
print("E) Teardown module. Should run AFTER all tests of this class")
24+
print("E) Teardown class. Should run AFTER all tests of this class")
2725

28-
def setup_method(method):
29-
print("C) Setup method. Should run BEFORE each test")
26+
def setup_method(self):
27+
print("C) Setup method. Should run BEFORE each test in this class")
3028

31-
def teardown_method(method):
32-
print("D) Teardown method. Should run AFTER each test")
29+
def teardown_method(self):
30+
print("D) Teardown method. Should run AFTER each test of this class")
3331

3432
def test_my_variable_value(self):
3533
assert self.my_variable == "OK!"
+11-18
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,20 @@
11
"""C1 - Test with Fixtures
2-
Fixtures are functions that run before the test where used, and return something
3-
https://docs.pytest.org/en/latest/fixture.html
4-
"""
2+
Fixtures are functions that run before the test where used, and can return something,
3+
injected on the test as a function parameter.
4+
https://docs.pytest.org/en/latest/fixture.html"""
55

66
import random
77
import pytest
8-
from pymongo import MongoClient
9-
from pymongo.collection import Collection
108

119

1210
@pytest.fixture
13-
def mongo_collection():
14-
"""This fixture will create a MongoClient instance and return a collection where we can write/read documents
15-
"""
16-
client = MongoClient("mongodb://127.0.0.1:27017")
17-
return client["test_database"]["test_collection"]
11+
def random_number():
12+
"""This fixture will return a random number"""
13+
print("A) Fixture runs")
14+
return random.randint(0, 1000)
1815

1916

20-
def test_insert_read_delete(mongo_collection: Collection):
21-
number = random.randint(0, 1000000)
22-
data = {"random_number": number}
23-
24-
result = mongo_collection.insert_one(data)
25-
26-
read = mongo_collection.find_one({"_id": result.inserted_id})
27-
assert read["random_number"] == number
17+
def test_random_number_fixture(random_number):
18+
print("B) Test starts")
19+
assert type(random_number) == int
20+
print("C) Test ends with number", random_number)
+16-45
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,30 @@
11
"""C2 - Test with Yield Fixtures
22
Yield fixtures use the "yield" statement to return something to the test function,
33
but they keep running after the test (similarly to context managers).
4-
We run 2 identical tests to verify that the data inserted by previous tests is deleted on the fixture.
54
https://pytest.readthedocs.io/en/2.9.1/yieldfixture.html
6-
https://docs.pytest.org/en/latest/yieldfixture.html
7-
"""
5+
https://docs.pytest.org/en/latest/yieldfixture.html"""
86

97
import random
108
import pytest
11-
from pymongo import MongoClient
12-
from pymongo.collection import Collection
139

1410

1511
@pytest.fixture
16-
def mongo_collection():
17-
"""This fixture will create a MongoClient instance and return a collection where we can write and read data.
18-
The collection is returned with yield statement to the test.
19-
After test run, all documents from the collection are deleted.
20-
"""
21-
client = MongoClient("mongodb://127.0.0.1:27017")
22-
collection = client["test_database"]["test_collection"]
12+
def random_number():
13+
"""This fixture will return a random number"""
14+
print("A) Fixture starts")
15+
number = random.randint(0, 1000)
16+
yield number
17+
print("D) Fixture ends")
2318

24-
# Delete all documents on collections before test run
25-
# (to clean possible documents from previous tests)
26-
collection.delete_many({})
27-
print("Deleted all documents in Mongo test collection!")
2819

29-
print("Returning collection to the test")
30-
yield collection
20+
def test_random_number_fixture_yield(random_number):
21+
print("B) Test starts")
22+
assert type(random_number) == int
23+
print("C) Test ends with number", random_number)
3124

32-
# Delete all documents on collection after test run
33-
# (to keep it clean for next tests)
34-
collection.delete_many({})
3525

36-
37-
@pytest.fixture
38-
def sample_data():
39-
return {"random_number": random.randint(0, 100000)}
40-
41-
42-
def test_insert_read_delete_1(mongo_collection: Collection, sample_data):
43-
read = mongo_collection.find({})
44-
assert list(read) == []
45-
46-
result = mongo_collection.insert_one(sample_data)
47-
48-
read = mongo_collection.find_one({"_id": result.inserted_id})
49-
assert read["random_number"] == sample_data["random_number"]
50-
51-
52-
def test_insert_read_delete_2(mongo_collection: Collection, sample_data):
53-
read = mongo_collection.find({})
54-
assert list(read) == []
55-
56-
result = mongo_collection.insert_one(sample_data)
57-
58-
read = mongo_collection.find_one({"_id": result.inserted_id})
59-
assert read["random_number"] == sample_data["random_number"]
26+
def test_random_number_fixture_fails(random_number):
27+
"""Even if a test fails, the fixture runs the code after the yield statement"""
28+
print("B) Test starts")
29+
assert type(random_number) == str
30+
print("C) Test ends (it should not reach this point!)")
+8-8
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
"""C3 - tmp_path built-in fixture
22
Usage of the built-in tmp_path fixture, which provides a temp path to save/read files into/from
3-
https://docs.pytest.org/en/latest/tmpdir.html
4-
"""
3+
https://docs.pytest.org/en/latest/tmpdir.html"""
54

65
from pathlib import Path
76

87

98
def test_write_read_file(tmp_path: Path):
10-
directory = tmp_path / "subdirectory"
11-
directory.mkdir()
12-
file_name = directory / "hello.txt"
9+
"""Create a file 'hello.txt' within the tmp_path, and write lines into it.
10+
Then, read the file lines and verify that written lines are on the read file."""
11+
file_path = tmp_path / "hello.txt"
12+
file_path = file_path.as_posix()
1313
lines = [
1414
"Hello there!",
1515
"General Kenobi!"
1616
]
1717

18-
print("The filename is", file_name.as_posix())
18+
print("The file path is", file_path)
1919

2020
# Write
21-
with open(file_name.as_posix(), "w") as file:
21+
with open(file_path, "w") as file:
2222
file.writelines("\n".join(lines))
2323

2424
# Read
25-
with open(file_name.as_posix(), "r") as file:
25+
with open(file_path, "r") as file:
2626
file_content = file.read()
2727
for line in lines:
2828
assert line in file_content

‎examples/D_class_inherit/__init__.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
"""Init is required to do relative import of BaseTest from test modules
2-
"""
1+
"""Init is required to do relative import of BaseTest from test modules"""

0 commit comments

Comments
 (0)