Evaluating error strings for 'unittest' assert methods.

Ethan Furman ethan at stoneleaf.us
Wed Apr 6 19:54:01 EDT 2016


On 04/06/2016 03:58 PM, John Pote wrote:
> I have been writing a very useful test script using the standard Python
> 'unittest' module. This works fine and has been a huge help in keeping
> the system I've been writing fully working even when I make changes that
> could break many features of the system. eg major rewrite of the
> interrupt routines. The target system in question is written in 'C' and
> runs on a 50p microcontroller at the end of a serial line.
>
> I like each assert...() to output helpful information when things go
> wrong. So I have put in quite complicated code to generate the error
> string the assert() method uses only when things go wrong. The normal
> case, when everything is working, means that all these error strings are
> constructed only to be discarded immediately when the assert() detects
> the test result is correct and no exception is throw.
>
> To my mind this seems a waste and adding unnecessary delays in the
> running of the whole test script.
>
> So I was wondering if there was some convienient, Python, way of calling
> an assert() method so the error string is only evaluated/constructed if
> the assert() fails and throws an exception. For example,
>
> self.assertEqual( (nBytes,expectedValues), (nBytesRd,valuesRead),
>              """Unexpected reg value.
> Expected values nBytes:%02x (%s)
> """%(nBytes,' '.join( [ "%04x"%v for v in expectedValues] )) +
> "Read values     nBytes:%02x (%s)"%(nBytesRd,' '.join( [ "%04x"%v for v
> in valuesRead] ))
>          )

My first thought is don't sweat it, it probably doesn't matter.  You 
could test this by timing both before and after removing all the custom 
error messages.

If you do want to sweat it -- maybe a custom object that replaces the 
__str__ and __repr__ methods with the code that creates the message?  No 
idea if it would work, but it would look something like:

class DelayedStr(object):  # pesky 2/3 code ;)
     def __init__(self, func):
         self.func = func
     def __str__(self):
         return self.func()

and your assert would look like:


self.assertEqual(
         (nBytes,expectedValues),
         (nBytesRd,valuesRead),
         lambda : """Unexpected reg value.
         Expected values nBytes:%02x (%s)""" %
             (nBytes,' '.join( [ "%04x"%v for v in expectedValues] )) +
             "Read values     nBytes:%02x (%s)"%(nBytesRd,
              ' '.join( [ "%04x"%v for v in valuesRead] )))

Certainly no more readable, but /maybe/ more performant.  (Assuming it 
even works.)

--
~Ethan~



More information about the Python-list mailing list