[Tutor] mock

Alex Kleider akleider at sonic.net
Fri Jan 22 15:27:24 EST 2016


Some weeks (perhaps months) ago, I posted a question about testing
and got many responses but had trouble grasping the concepts
so I settled on the suggestion that I thought would be the easiest
to implement (using unittest.mock.) Here it is-

"""
from Peter Otten:
I find Ben's example instructive, but when you're just starting you
might prefer a simpler approach:

import unittest
from unittest import mock
import mycode

class TestCollectData(unittest.TestCase):
     def test(self):
         with mock.patch(
                 "builtins.input",
                 side_effect=["foo", "bar", "baz"]):
             self.assertEqual(
                 mycode.collect_data(),
                 dict(first="foo", last="bar", phone="baz"))

if __name__ == "__main__":
     unittest.main()
"""

I've successfully implemented mock.patch parameters but don't
understand how to implement the parameters for the assertEqual
call as it pertains to my particular situation.

My current code sort of does the job but determinating success or
failure is not really automated.

It's Python 3 on Ubuntu 14.4LTS.
My test code and the tested code follow.  Any suggestions would be
most welcome.  Thanks in advance.
Alex

Test Code:
#!../../venv/bin/python
# -*- coding: utf-8 -*-
# vim: set file encoding=utf-8
"""
Attempt to automate testing of menu module.
"""
import unittest
from unittest import mock
import menu

class MenuTests(unittest.TestCase):

     def test_create_new(self):
         with mock.patch("builtins.input",
                 side_effect=['1', 'testentity', '', '0']):
             menu.menu()

if __name__ == '__main__':  # code block to run the application
     unittest.main()


Tested code:

#!../../venv/bin/python
# -*- coding: utf-8 -*-
# vim: set file encoding=utf-8
"""
A simple menu model used to format a question
about how to test using unittest.mock.
"""

class Entities(object):
     """
     Keep track of the entities including a default.
     Expect only one instance which will be a global.
     """

     def __init__(self, list_of_entities, default=''):
         self._list = list_of_entities
         self.default = default

     def get_default(self):
         return self.default

     def reset_default(self, default=''):
         self.default = default

     def get_list(self):
         return self._list

     def add(self, new_entity, set_default=False):
         """
         Adds another entity returning it to signal success.
         Returns None if entity is unacceptable.
         """
         if (not new_entity in self._list
         and new_entity.isalnum()
         and new_entity[0:1].isalpha()):
             self._list.append(new_entity)
             if set_default:
                 self.reset_default(new_entity)
             return new_entity
         else:
             print("Failing to add an invalid entity: '{}'."
                         .format(new_entity))

     def get_new_entity(self):
         """
         Prompts user for a new entity which, if valid, is
         returned after being appended to list and set as default.
         Returns None if fails to create a new entity.
         """
         while True:
             new_entity = input("Pick name for new entity: ")
             if not new_entity: return
             if new_entity != self.add(new_entity, set_default=True):
                 continue
             else:
                 return new_entity

     def remove(self, entity2remove):
         if not entity2remove in self.get_list():
             print("Can't remove '{}': not in the list."
                         .format(entity2remove))
             return
         if entity2remove == self.get_default():
             self.reset_default()
         self._list = [entity for entity in self._list
                         if entity!=entity2remove]

     def show_listing(self):
         """
         Returns a string listing the available entities.
         Empty string if there are no entities.
         """
         return ''.join(["\n\t    {}".format(entity)
                         for entity in self.get_list()])

     def entity_choice(self, set_default=False):
         """
         Prompts the user to choose from the list.
         Returns a valid choice or None.
         Can optionally set the default to the chosen entity.
         """
         list_of_entities= self.get_list()
         if not list_of_entities:
             print("There are no entities from which to choose.")
             return
         default_line = ''
          if self.default:
             default_line = ("\n\t_: Default is '{}', just hit enter."
                                 .format(self.default))
         menu = ('\n'.join(["\t{}: {}".format(n, entity)
                 for (n, entity) in enumerate(list_of_entities, 1)])
                 + default_line)
         while True:
             option = input(
     """Choose one of the following:
     {}
     \t0: to exit.
     Pick an entity: """.format(menu))
             default = self.get_default()
             if (option=='' or option=='_') and default:
                 return default
             try:
                 option = int(option)
             except ValueError:
                 print("Invalid option: {}! (It must be an integer.)"
                         .format(option))
                 continue
             entity_list = self.get_list()
             if option in range(1, len(entity_list) + 1):
                 choice = entity_list[option - 1]
                 if set_default:
                     self.default = choice
                 return choice
             elif option == 0:
                 return None
             else:
                 print("Invalid entry- try again ('0' to quit.)")

def create_new(option, entities):
     """
     A main menu response function.
     """
     print("Picked '{}. Create a new entity.'".format(option))
     entity = entities.get_new_entity()
     if entity:
         print(
             "Entity '{}' successfully created (and set as default.)"
                 .format(entity))
         work_with(entity)
     else:
         print("Aborting entity creation.")

def choose_existing(option, entities):
     """
     A main menu response function.
     """
     print("Picked '{}'. Choose an entity."
             .format(option))
     choice = entities.entity_choice(set_default=True)
     if choice:
         work_with(choice)

def delete_option(option, entities):
     """
     A main menu response function.
     """
     print("Picked '{}. Delete an existing entity.'".format(option))
     while True:
         entity = entities.entity_choice()
         if not entity:
             print("Entity deletion aborted.")
             return
         y_n = input("About to delete entity '{}', ARE YOU SURE? "
                 .format(entity))
         if y_n and y_n[0] in 'Yy':
             print("Deleting entity '{}'.".format(entity))
             entities.remove(entity)
         else:
             print("No deletion being done.")
         break

def work_with(entity):
     """
     Provides an interface stub for once an entity has been selected.
     """
     _ = input("Stub of code to work with '{}' entity goes here."
                     .format(entity))

def menu():
     """
     Provides the top level user interface.
     """
     entities = Entities(["ent1", "ent2", "ent3"], "ent2")
     while True:
         listing = entities.show_listing()
         if listing:
             listing = (
"""\n(  Currently existing entities are: {}          )"""
                     .format(listing))
         option = input("""
Main Menu:{}
     1. Create a new entity.
     2. Choose an existing entity.
     9. Delete an entity.
     0. Exit
Choice: """
                         .format(listing))
         print("Main Menu choice: {}".format(option))
         if option in ('', '_', '0'):
             return
         try:
             option = int(option)
         except ValueError:
             print(
                 "Invalid main menu choice: {} (must be an integer.)"
                         .format(option))
             continue
         if option == 1:
             entity = create_new(option, entities)
         elif option == 2:
             entity = choose_existing(option, entities)
         elif option == 9:
             delete_option(option, entities)
             entity = ''
          else:
             print("BAD CHOICE '{}'- try again....".format(option))
             entity = None
         if entity:
             work_with(entity)

if __name__ == "__main__":
     menu()
~



More information about the Tutor mailing list