Skip to content

Commit de83649

Browse files
committed
Fixed deprecated py.test funcargs example. Completed mock unit test example. Up Next: How much testing is enough?
1 parent e27ab9d commit de83649

2 files changed

Lines changed: 81 additions & 5 deletions

File tree

ch12/funcargs_pytest.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
from stats import StatsList
2+
import pytest
23

34
"""
4-
Demonstration of using the py.test funcargs feature to set up variables.
5+
Demonstration of using the py.test framework @pytest.fixture() feature to set up
6+
variables.
57
"""
68

79

8-
def pytest_funcarg__valid_stats(request):
10+
#def pytest_funcarg__valid_stats(request): # this is deprecated
11+
@pytest.fixture
12+
def valid_stats(request):
913
"""
1014
Global test configuration. This function can also be placed in conftest.py
11-
which is parsed by py.test. Funcargs must be named according to the
12-
convention pytest_funcarg__<identifier>, where <identifier> is a valid
13-
variable name that can be used as a parameter in a test function.
15+
which is parsed by py.test.
1416
:param request:
1517
:return: object to be passed as an argument into the individual test
1618
functions.

ch12/mock_flightstatus.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import datetime
2+
import redis
3+
import pytest
4+
from unittest.mock import Mock
5+
from unittest.mock import patch
6+
7+
"""
8+
Demonstration of mocking in unit testing using unittest.mock standard library.
9+
10+
The Mock() class removes the need to create stubs throughout the test suite.
11+
After performing any action, assertions can be made about which methods or
12+
attributes were used and the arguments they were called with.
13+
"""
14+
15+
16+
class FlightStatusTracker:
17+
"""
18+
This class is responsible for tracking flight statuses in a key-value store
19+
so that the timestamp and the most recent status can be stored.
20+
"""
21+
ALLOWED_STATUSES = {'CANCELLED', 'DELAYED', 'ON TIME', 'ARRIVED'}
22+
23+
def __init__(self, redis_instance=None):
24+
self.redis = redis_instance if redis_instance else redis.StrictRedis()
25+
26+
def change_status(self, flight, status):
27+
status = status.upper()
28+
if status not in self.ALLOWED_STATUSES:
29+
raise ValueError('{} is not a valid status'.format(status))
30+
31+
key = 'flightno:{}'.format(flight)
32+
value = '{}|{}'.format(datetime.datetime.now().isoformat(), status)
33+
self.redis.set(key, value) # This should be tested
34+
35+
36+
@pytest.fixture
37+
def tracker():
38+
return FlightStatusTracker()
39+
40+
41+
def test_mock_method(tracker):
42+
"""
43+
Test that the flight status is not valid and that it was not set in the
44+
database.
45+
:param tracker: FlightStatusTracker object under test.
46+
:return: None
47+
"""
48+
tracker.redis.set = Mock() # create mock object for the redis.set() method
49+
with pytest.raises(ValueError) as ex:
50+
tracker.change_status('AC101', 'lost')
51+
assert ex.value.args[0] == 'LOST is not a valid status'
52+
assert tracker.redis.set.call_count == 0
53+
54+
55+
def test_patch(tracker):
56+
"""
57+
Demonstration of the unittest.mock.patch() method used to mock the standard
58+
library datetime object.
59+
:param tracker: FlightStatusTracker object under test.
60+
:return: None
61+
"""
62+
tracker.redis.set = Mock()
63+
# Create a known datetime for test purposes
64+
fake_now = datetime.datetime(2015, 4, 1)
65+
# Mock the standard library datetime to return the known test value
66+
with patch('datetime.datetime') as dt:
67+
dt.now.return_value = fake_now
68+
tracker.change_status('AC102', 'on time')
69+
dt.now.assert_called_once_with() # Make sure the datetime.now() is called
70+
# Make sure the redis mock object was called with the correct parameters
71+
tracker.redis.set.assert_called_once_with(
72+
'flightno:AC102',
73+
'2015-04-01T00:00:00|ON TIME'
74+
)

0 commit comments

Comments
 (0)