#!/usr/bin/env PYTHONHASHSEED=1234 python3 # Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Reproduce book environment import random random.seed(1234) import logging from pprint import pprint from sys import stdout as STDOUT # Write all output to a temporary directory import atexit import gc import io import os import tempfile TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) # Make sure Windows processes exit cleanly OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() atexit.register(close_open_files) # Example 1 class ZooDatabase: def get_animals(self, species): pass def get_food_period(self, species): pass def feed_animal(self, name, when): pass # Example 2 from datetime import datetime def do_rounds(database, species, *, utcnow=datetime.utcnow): now = utcnow() feeding_timedelta = database.get_food_period(species) animals = database.get_animals(species) fed = 0 for name, last_mealtime in animals: if (now - last_mealtime) >= feeding_timedelta: database.feed_animal(name, now) fed += 1 return fed # Example 3 from unittest.mock import Mock database = Mock(spec=ZooDatabase) print(database.feed_animal) database.feed_animal() database.feed_animal.assert_any_call() # Example 4 from datetime import timedelta from unittest.mock import call now_func = Mock(spec=datetime.utcnow) now_func.return_value = datetime(2019, 6, 5, 15, 45) database = Mock(spec=ZooDatabase) database.get_food_period.return_value = timedelta(hours=3) database.get_animals.return_value = [ ('Spot', datetime(2019, 6, 5, 11, 15)), ('Fluffy', datetime(2019, 6, 5, 12, 30)), ('Jojo', datetime(2019, 6, 5, 12, 55)) ] # Example 5 result = do_rounds(database, 'Meerkat', utcnow=now_func) assert result == 2 database.get_food_period.assert_called_once_with('Meerkat') database.get_animals.assert_called_once_with('Meerkat') database.feed_animal.assert_has_calls( [ call('Spot', now_func.return_value), call('Fluffy', now_func.return_value), ], any_order=True) # Example 6 try: database.bad_method_name() except: logging.exception('Expected') else: assert False # Example 7 DATABASE = None def get_database(): global DATABASE if DATABASE is None: DATABASE = ZooDatabase() return DATABASE def main(argv): database = get_database() species = argv[1] count = do_rounds(database, species) print(f'Fed {count} {species}(s)') return 0 # Example 8 import contextlib import io from unittest.mock import patch with patch('__main__.DATABASE', spec=ZooDatabase): now = datetime.utcnow() DATABASE.get_food_period.return_value = timedelta(hours=3) DATABASE.get_animals.return_value = [ ('Spot', now - timedelta(minutes=4.5)), ('Fluffy', now - timedelta(hours=3.25)), ('Jojo', now - timedelta(hours=3)), ] fake_stdout = io.StringIO() with contextlib.redirect_stdout(fake_stdout): main(['program name', 'Meerkat']) found = fake_stdout.getvalue() expected = 'Fed 2 Meerkat(s)\n' assert found == expected