[TIP] Dilemma with using mock objects

Herman Sheremetyev herman at swebpage.com
Mon Apr 23 21:14:42 PDT 2012


Hi Ted,

I think these are good questions. I don't know if there is a clear
"right" answer, and intelligent people often disagree on this topic,
but from personal experience I would say you want to make your code
modular, and therefore testable, first, and worry about what to mock
only when, and if, it becomes a necessity.

In a script like the one you describe, I would try to group all the
side-effecty IO related code into a single place and isolate it as
much as possible from the logic of the rest of the program. That way,
if you provide a single entry point or a wrapper for the IO code, you
will be able to easily test the flow of the program and not have to
worry about complicated mocking of built-in functions.

At the end of the day, how many tests you write and what you test is
going to be a trade-off. You'll never be able to test every possible
exception case or control flow, so identify the important failure
cases, the success cases, and add however many tests you feel will
help you actually get this code into production and maintain it in the
future. This usually means identifying the main API entry points and
testing for specific results given specific inputs rather than setting
up an elaborate mock structure inside the tests that mirrors your
production code or cherrypick mocks dangerous calls (that way lies
madness).

HTH,

-Herman

On Tue, Apr 24, 2012 at 7:24 AM, Tu Ted <999iscool at gmail.com> wrote:
> Please bear with me. Suppose I want to assert that result is in a particular
> format, I might set the return value to something as (True, 'Success')
>
> Sample code: http://pastebin.com/VraxaKjL
>
> def batch_move(self, *args, **kwargs):
>   '''
>   Move a batch of files to its respective destinations.
>   Return type: tuple (boolean, message)
>                       T/F    , 'string' / str(exception)
>   '''
>
>   srcs= kwargs.get('srcs', None)
>   dests = kwargs.get('dests', None)
>
>   try:
>     if srcs and dests:
>        # map srcs and dests into dictionary (srcs --> keys, dests -->
> values)
>        src_dest= dict(zip(srcs, dests))
>
>        for src, dest in src_dest:
>          if os.path.exists(src):
>            if os.path.exists(dest):
>               shutil.rmtree(dest)
>            shutil.move(src, dest)   # either way we will proceed to move
>          else:
>            return (False, '%s does not exist!' % src)
>        return (True, 'Success!')
>
>     else:
>        return (False, 'Something gone wrong with those kwargs...')
>   except Exception as e:
>     return (False, e)
>
> In order to get to return (True, 'Success!'), I have
>
> 1. patch os.path.exists with True as return value. But in one unittest I
> want to skip this, how do I patch that os.path.exists?
>         if os.path.exists(dest):  # I want to skip this
>              shutil.rmtree(dest)
>
>
>
> 2. How do I patch shutil.move(src, dest)? Do I just give True so it doesn't
> generate error? What if I want the case it fails and caught an exception?
> How do I simulate that? (I wouldn't always know which exception to catch,
> the primarily reason to use Exception as e).
>
> 3. If I actually pass the function, does it really mean no exception caught
> and it went through every single line? Or is it because I set
> `mock_object.return_value = (True, 'Success!')?
>
> 4. I am only using two dependencies here, do I need to patch out all the
> external dependencies such as (os, sys, math, datetime) all in one? Or if my
> function is using other functions (which are refactored)
>
>
>  def f1(*args, **kwargs):
>     f2(..)   # use math, plot, datetime
>     f3(..)   # use math and datetime
>     f4(..)   # use datetime
>
>
> Thanks. Sorry for the long questions. I really want to be good at writing
> unittests.
>
>
> --
> Ted
>
>
>
> _______________________________________________
> testing-in-python mailing list
> testing-in-python at lists.idyll.org
> http://lists.idyll.org/listinfo/testing-in-python
>



More information about the testing-in-python mailing list