doctests for interactive functions

Brian van den Broek broek at cc.umanitoba.ca
Thu Feb 8 17:28:21 EST 2007


Ben Finney said unto the world upon 02/08/2007 03:03 PM:
> Brian van den Broek <broek at cc.umanitoba.ca> writes:
> 
>> Since the classes are for getting input interactively, a
>> straightforward use of doctest is not going to work. (The tests
>> won't run automatically.) I've come up with a way to make it work,
>> but I would like to know from more experienced people if there are
>> better ways that I am overlooking.
> 
> One suggestion I can give is to decouple the task of "get input from
> the user" and "validate this value against particular criteria". The
> former should be so simple and *stupid* in its implementation that
> very little can go wrong. The latter should be each of your validation
> classes, or some re-implementation of them, that is non-interactive
> and therefore easy to test.
> 
> Then, the application simply uses these building blocks (that are now
> well-tested and reliable) to do what it does: get input, then validate
> that input, and possible ask for input again.
> 


Hi Ben and all,

Thanks for the suggestion.

I'm a uni-lingual hobbyist Python programmer; consequently, I'm not 
positive I grasp your intent. In particular, as I want invalid input 
to cause a reprompting of the user, I am unsure how to decouple as you 
suggest.

Can I run the rough structure of my code past you to see if it is in 
the vicinity of what you mean? (I have removed some details for sake 
of a short(er :-)) post.)


My .get method looks like:

def get(self, input_function=raw_input):
     while True:
         self._prompt_user()
         self._input = input_function()
         if self._is_valid_input():
             break
         else:
             self._process_invalid_input()
     self._set_data()

The base class ._prompt_user just displays a prompt. Individual 
subclasses might implement ._prompt_user to both display a prompt, and 
further information about constraints on valid inputs, or generate a 
menu of options on the fly, etc.

Subclasses implement ._is_valid_input to return True if the input 
meets the desired constraints, False otherwise. So, 
YesNo._is_valid_input ensures that ._input.lower() is in ['y', 'n', 
'yes', 'no'], for instance.

._process_invalid_input is implemented to provide useful feedback 
about invalid input. So, YesNo._process_invalid_input() emits a 
reminder that a value in ['y', 'n', 'yes', 'no'] is needed, for instance.

._set_data is usually implemented to just store the user's input as 
.data, but in some cases, it first subjects it to further processing. 
For instance YesNo._set_data sets .data to True if the user entered a 
yes value, False if they entered a no value.

Is this the sort of thing you mean, or is this the sort of coupling 
you suggest I avoid?

I do believe I can test each bit separately as the code is currently 
organized. My original concern was how to best create doctests that 
could also serve as illuminating examples in the documentation. In 
that context, I don't want implementation detail methods tested 
directly, but instead I desire to show the user how to actually use 
the class. (If it matters, I am writing the documentation in LaTeX, 
using doctest.testfile to run the examples in my .tex file.)

Thanks and best,

Brian vdB




More information about the Python-list mailing list