[TIP] 5 lines of code equals 30+ lines of single test

John Wong gokoproject at gmail.com
Wed Jul 18 11:46:39 PDT 2012


Thanks for all the active responses. I am grateful. Thank you very much.
Yes. It's very difficult for me, especially there is not enough support
from education to show what is good or bad when it comes to testing code.
So sad...

I am going to respond in this one email instead of separate email per
response:

1. I think two things are gained from doing mocking:
      (1) isolate dependices, make your system under test in vacuum state
      (2) when you patch out all the dependencies, you can run 100+ tests
very quickly

When I don't patch out every dependence, tests are slow. They can take 4-5
seconds (wow big deal). But when you do patch all of them out, they can run
in 1 second. I am probably amazed by such demonstration in some PyCon
videos.


>> why kwargs is used?
There are two decorators applied to this function, but I left them out. The
kwargs contains more than one key in the real code. But for demo purpose, I
am showing only one.

>> break the code into small pieces
  My issue with breaking up this sample code into two smaller pieces is
that the the partitions are not always resuable. It makes reading harder
(you'd jump to another function to find out what's going there). As
straightforward as it is, I personally think this function can stay as it
is. If it's 3-lines, and all it does it read the path, open the file,
return the content, do we still break into two parts? I think that's not
really a good idea. I think you guys are merely showing me that sometimes
we can break down a function into smaller parts. I try to do that as much
as possible :)

2. I understand that patching everything out is bad,especially pure Python
std functions such as `open`, or `os.path.join`.
So is this what we should do?

Suppose the sample code is:
def readfile(*args, **kwargs):
    local = kwargs.get('local', os.getcwd())
    filename = args[0]
    f_path = os.path.join(local, filename)
    with open(f_path, 'r') as f:
        return f.read()

Let's have three tests

@patch.object('os.path', 'join')
def test01_local_is_used(self, mk_join):
    kwargs = {'local': 'LOCAL'}
    mk_join.return_value = 'full path'
    readfile('filename', **kwargs)
    assert mk_join.call_args_list == [call('LOCAL', 'filename')]

Another test will tests that `open` is called with f_path and `r` ?
Only patch the function that your test is actually depending on? In the
first time, we want to know local is used, and the immediate use of this
`local` variable is os.path.join. So patching out os.path.join seems
reasonable?

Thanks.



def test01_local_is_used:
     read


On Wed, Jul 18, 2012 at 10:18 AM, Ned Batchelder <ned at nedbatchelder.com>wrote:

>  On 7/18/2012 12:58 AM, John Wong wrote:
>
> Hi guys,
>
> Here is a simple function.
>
> def readfile(*args, **kwargs):
>     local = kwargs.get('local', os.getcwd())
>     filename = args[0]
>     f_path = os.path.join(local, filename)
>     with open(f_path, 'r') as f:
>         return f.read()
>
>
> Now here is just ONE case of the readfile test code. For readabilty, I
> will put in pastebin.com
> http://pastebin.com/P45uc2TV
>
> I am verifying how many times X Y C dependencies are called and what
> parameters are passed to them. I think these are necessary to check (what
> if one of the dependency functions called with the wrong variable).
>
>  You definitely don't have to mock os.path.join.   You don't care that it
> called that function, you care that it opened the right path, and you
> already have a check for that.  You're over-testing the implementation of
> the function.  Focus on the externally-observable behavior.
>
> If you are checking call_args_list, then .called or .call_count is
> redundant, so remove those lines from the test.
>
> BTW: the function itself is a bit odd.  Why use **kwargs if you only use
> one keyword, and *args if you only use the first element?
>
> --Ned.
>
>  But is this really the nature of unittest? I've read many tests code and
> they all seen to be 5-20 lines..... I faint when I am reading my test
> code.............. SIGH
>
> I have some 10 lines function and it took 50 lines to test! Let alone for
> each of these functions, I need to write a a few tests for each branch
> case...
> So if there are 10 functions, each 5 lines, I can end up with 1000 lines
> of test code....
>
> Last question, I find my patching is really really long and inconvince.
> How should I patch (say they all come from os package)?
>
> Thanks.
> John
>
>
> _______________________________________________
> testing-in-python mailing listtesting-in-python at lists.idyll.orghttp://lists.idyll.org/listinfo/testing-in-python
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.idyll.org/pipermail/testing-in-python/attachments/20120718/e5e1a5b2/attachment.htm>


More information about the testing-in-python mailing list