{"id":194,"date":"2010-01-24T23:20:39","date_gmt":"2010-01-25T05:20:39","guid":{"rendered":"http:\/\/www.insomnihack.com\/?p=194"},"modified":"2010-01-24T23:20:39","modified_gmt":"2010-01-25T05:20:39","slug":"python-unit-testing-with-mock","status":"publish","type":"post","link":"http:\/\/www.insomnihack.com\/?p=194","title":{"rendered":"Python Unit Testing with Mock"},"content":{"rendered":"<p>In the past, while creating unit tests for C# classes, I&#8217;ve frequently used a mock object framework such as <a href=\"http:\/\/code.google.com\/p\/moq\/\">Moq<\/a> to isolate classes under test. When I started working in Python, I initially didn&#8217;t think I&#8217;d need such a framework to write my tests. Creating a stub with a given set of attributes and methods is easy in language like Python.<\/p>\n<p>However, I soon noticed I was writing a lot of these little stubs, and this was &#8216;dirtying up&#8217; my nice, readable tests. The next problem I encountered was 3rd party libraries with more complex interfaces, like this <a href=\"http:\/\/mike.verdone.ca\/twitter\/\">Twitter library<\/a>. It contains a client class that dynamically wraps Twitter&#8217;s <a href=\"http:\/\/apiwiki.twitter.com\/\">RESTful API<\/a>. So instead of calling <a href=\"http:\/\/apiwiki.twitter.com\/Twitter-REST-API-Method%3A-statuses%C2%A0friends\">http:\/\/twitter.com\/statuses\/friends.format<\/a>, you call Twitter.statuses.friends(). The Twitter client doesn&#8217;t have a <em>statuses.friends<\/em> method defined, it creates the URL dynamically from the dot notation you enter.<\/p>\n<p>Creating fakes to test code using such a class would be tedious. Additionally, I was looking at having to pass in instances of other 3rd party classes I was using. The whole thing was getting dangerously close to the DI\/IoC mess we&#8217;d left behind when we switched from C#. As <a href=\"http:\/\/www.aqua-web.com\/\">Jonathon<\/a> commented a few weeks back:<\/p>\n<blockquote><p>In Python, hard is a smell.<\/p><\/blockquote>\n<p>Enter <a href=\"http:\/\/www.voidspace.org.uk\/python\/mock\/\">Mock<\/a>. Mock is a mocking and testing library for Python by Michael Foord. Unlike many of the other mocking libraries for Python that use the record\/replay pattern, Mock uses an action\/assert pattern similar to Moq.<\/p>\n<p>Below is a snippet of code that uses the previously mentioned Twitter RESTful client with the dynamic API methods. I&#8217;d like to mock up the actual API calls so that I&#8217;m not connecting to Twitter when I run my tests.<\/p>\n<p>[python]<br \/>\nfrom twitter import Twitter, TwitterError<\/p>\n<p>class TwitterClient(object):<\/p>\n<p>    def __init__(self, login, password):<br \/>\n        self._twitter = Twitter(login, password)<\/p>\n<p>    def all_friends(self):<br \/>\n        return self._get_statuses(<br \/>\n            lambda crs: self._twitter.statuses.friends(cursor = crs))<\/p>\n<p>    def all_followers(self):<br \/>\n        return self._get_statuses(<br \/>\n            lambda crs: self._twitter.statuses.followers(cursor = crs))<\/p>\n<p>    def _get_statuses(self, call):<br \/>\n        crs = -1<br \/>\n        results = []<br \/>\n        while crs != 0:<br \/>\n            page = call(crs)<br \/>\n            users = page[&#8216;users&#8217;]<br \/>\n            crs = page[&#8216;next_cursor&#8217;]<br \/>\n            results.extend(users)<br \/>\n        return results<br \/>\n[\/python]<\/p>\n<p>Mock makes this easy.<\/p>\n<p>[python]<br \/>\nclass TwitterClientTests(unittest.TestCase):<\/p>\n<p>    def test_all_friends(self):<br \/>\n        &quot;&quot;&quot;Verify TwitterClient.all_friends()&quot;&quot;&quot;<br \/>\n        tc = TwitterClient(&#8216;username&#8217;, &#8216;password&#8217;)<br \/>\n        tc._twitter = Mock()<br \/>\n        tc._twitter.statuses.friends.return_value = \\<br \/>\n            {&#8216;users&#8217;:[{&#8216;a&#8217;:1}], &#8216;next_cursor&#8217;:0}<br \/>\n        result = tc.all_friends()<br \/>\n        tc._twitter.statuses.friends.assert_called_with(cursor = -1)<br \/>\n        self.assertEquals([{&#8216;a&#8217;:1}], result)<br \/>\n[\/python]<\/p>\n<p>After creating my test class, I mock out the actual internal Twitter instance that is created. I then set the return value for the API method that <em>all_friends<\/em> will pass to <em>_get_statuses<\/em>. I don&#8217;t need to reproduce the full JSON returned from a real Twitter API call, I just need enough to test a single loop through the cursor-based call. Once I&#8217;m finished, in addition to validating the results returned using regular TestCase asserts, I can verify that my mock method was called with the correct parameters.<\/p>\n<p>This just scratches the surface of what you can do with Mock. It contains facilities for patching, which is useful if you want to mock objects that get created during a method call (i.e. they aren&#8217;t easy-to-get-at class members like self.foo). Check out Mock&#8217;s <a href=\"http:\/\/www.voidspace.org.uk\/python\/mock\/#api-documentation\">documentation<\/a> and <a href=\"http:\/\/www.voidspace.org.uk\/python\/mock\/#introduction\">introduction<\/a> for more details.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the past, while creating unit tests for C# classes, I&#8217;ve frequently used a mock object framework such as Moq to isolate classes under test. When I started working in Python, I initially didn&#8217;t think I&#8217;d need such a framework to write my tests. Creating a stub with a given set of attributes and methods [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[6,14],"_links":{"self":[{"href":"http:\/\/www.insomnihack.com\/index.php?rest_route=\/wp\/v2\/posts\/194"}],"collection":[{"href":"http:\/\/www.insomnihack.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.insomnihack.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.insomnihack.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.insomnihack.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=194"}],"version-history":[{"count":0,"href":"http:\/\/www.insomnihack.com\/index.php?rest_route=\/wp\/v2\/posts\/194\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.insomnihack.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=194"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.insomnihack.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=194"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.insomnihack.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=194"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}