[Tutor] Testing print

Steven D'Aprano steve at pearwood.info
Fri Sep 30 06:07:08 EDT 2016


On Thu, Sep 29, 2016 at 09:24:51PM -0500, boB Stepp wrote:
> Testing output of print functions (Py 3).  First off, is it worth it to do so?

Unless you are writing tests for the Python language itself, you can 
assume that print() itself is working.

You should test functions that call print: call the function (or method) 
and see that it does print what you expect.

But, in general, you should not intermingle *calculation* and *output*. 
There may be a few examples, but functions should do one, or the other, 
not both. This makes it easy to reason about, re-use, and test, both 
functions.

For example, suppose you have a function that takes a single string, 
formats it in some way, and then displays it in a dialog box. You should 
split that into two:

- take a string and format it;
- take a formatted string and display it.

Same applies to print, writing to log files, or writing output to any 
other file.

> Second, it seems that prints are often intermingled with the main
> logic of a function and only serve to pass on a message to the user.

Yeah, you normally shouldn't do that. Imagine if every time you called 
len(something), the len function was chatty and decided to print 
something. It would make it really hard to print anything where len() is 
involved as part of the calculation. Say I want to print a formatted 
table:

    --------  --------  --------
    COLUMN 1  COLUMN 2  COLUMN 3
    --------  --------  --------
    alpha     second    gamma 
              letter
    --------  --------  --------

but got something like:


    hi this is len I just wanted to let you know I'm working hard 
-------- hi this is len I just wanted to let you know I'm working hard 
-------- hi this is len I just wanted to let you know I'm working hard 
--------
    hi this is len I just wanted to let you know I'm working hard COLUMN 
1 hi this is len oops something went wrong but that's okay I fixed it 
COLUMN 2 hi this is len I just wanted to let you know I'm working really 
hard you ought to be grateful okay COLUMN 3
    hi this is len I just wanted to let you know I'm working hard 
-------- hi this is len ...

etc. Chatty functions are *the worst*, unless they are specifically 
intended for reporting and output. Don't mix calculation and output.

Or rather, its okay to mix them, provided you have *at least* two 
separate functions:

(1) function that does the calculation;
(2) function that calls (1) and then does the output

and preferably three:

(1) function that does the calculation;
(2) function that does the output;
(3) function that calls (1) and then (2)


If (1) and (2) are well-designed, then (3) is so trivial it needs no 
tests:

def main():
    x = calculate(stuff)
    report(x)

but of course it's not always that simple. Nevertheless, that's the 
ideal you should aim for.


[...]
> def right_justify(a_string):
>     '''This fucntion will take the string, "a_string", and right justify it by
>     padding it with spaces until its last character falls in column 70 of the
>     display.  The padded string will be returned.'''
> 
>     if len(a_string) <= 70:
>         num_pad_spcs = 70 - len(a_string)
>         return (' ' * num_pad_spcs) + a_string
>     else:
>         print("The string has too many characters (> 70)!")
>         print("Only a partial, 70 character line will be returned.")
>         return a_string[:70]

Too chatty! It mixed output and calculation. Instead, you should just 
document what the function does ("pads or truncates the string to 
exactly 70 characters") and leave it at that, or if you must report on 
what it is doing, consider:

- print warning messages to stderr instead of stdout;

- write warnings to a log file;

- use the warnings module:

    import warnings
    warnings.warn("unable to pad string, as it is too long; truncating instead")

- raise an exception and stop processing.


[...]
> So (Even though this is a simple
> exercise.) should I test the prints?

If you don't test it, how do you know it works?


> Anyway, it would seem that the only way to capture the output of a
> print is to redirect the stdout to something I can capture and compare
> against.  Googling brings up some people doing something with mock
> objects, some redirecting to a string buffer, some to a file, some to
> other things.  What, in your opinion(s), is the cleanest way to handle
> this?

I find that using stringIO is the simplest and most obvious way to do 
it, although I don't have a lot of experience with the mock objects.



-- 
Steve


More information about the Tutor mailing list