Vorsprung
Vorsprung

Reputation: 34297

Trying to mock method in Object gives 'AttributeError'

I am trying to test a method in an already existing class. Within inputStreamThread method in the Foo.crawler.crawlerapp.CrawlerApp class the method addUrl is called.

inputStreamThread reads from stdin and then calls addUrl

addUrl is also in the CrawlerApp class

I was hoping to be able to use assert_called_with on a mock addUrl to check that inputStreamThread is doing the right thing and calling addUrl

Trouble is I cannot get the syntax right for the mock of addUrl within CrawlerApp

I used an example straight out of the mock docs but get the error shown below

As you can see I am also mocking stdin to be able to present test data on it

My question is, what code do I use to carry out this sort of test and not get the error shown?

import Foo.crawler.crawlerapp
from unittest import TestCase
from mock import patch, Mock
from mephistopheles.messageformat import EventDataFrame
from mephistopheles.messageformat.types import adservers as pbufs
import time
import sys


class testDeserial(TestCase):

    def generate_dummy_auction_event(self,url):
        adunitinfo = pbufs.AdUnitInfo(index_on_page=0, url=url)
        geoloc = pbufs.GeoLocation(country="DE", region="low")
        userinfo = pbufs.UserInfo(user_hash=1,
                                  ip_octets=1,
                                  geolocation=geoloc,
                                  language="en")
        auctioninfo = pbufs.AuctionInfo(timestamp=int(time.time()),
                                        user=userinfo,
                                        ad_unit=adunitinfo)
        return auctioninfo

    def setUp(self):
        pass

    @patch.object(Foo.crawler.crawlerapp.CrawlerApp,'addUrl')
    def test_check_url(self, MaddUrl):
        url_a = "http://audaxing.wordpress.com"
        dummy_event = self.generate_dummy_auction_event(url_a)
        with patch("sys.stdin") as mock_stdin:
            mock_stdin.read.return_value = dummy_event
            ca._running = True
            input_thread = threading.Thread(target=self.inputStreamThread)
            input_thread.start()
            time.sleep(0.5)
            ca._running = False
        MaddUrl.assert_called_with(url_a)

The test run output....

$ bin/tests --tests-pattern=test_deserialize
Test-module import failures:

Module: Foo.crawler.tests.test_deserialize

Traceback (most recent call last):
  File "/home/jamie/svn/Foo/crawler.buildout/trunk/src/Foo.crawler/Foo/crawler/tests/test_deserialize.py", line 11, in <module>
    class testDeserial(TestCase):
  File "/home/jamie/svn/Foo/crawler.buildout/trunk/src/Foo.crawler/Foo/crawler/tests/test_deserialize.py", line 28, in testDeserial
    @patch.object(Foo.crawler.crawlerapp.CrawlerApp,'addUrl')
AttributeError: 'function' object has no attribute 'object'



Test-modules with import problems:
  Foo.crawler.tests.test_deserialize
Total: 0 tests, 0 failures, 0 errors in 0.000 seconds.

Upvotes: 6

Views: 15985

Answers (2)

Vorsprung
Vorsprung

Reputation: 34297

I figured out what to do in the end. Bit of a monkey at a typewriter thing, not sure why I have to use "patch" and not "patch.object" or why I need to make Mock() objects first. I just tried every possible pattern from the examples in the docs

Anyway, this works for me

def test_check_url(self):
    url_a = "http://audaxing.wordpress.com"
    dummy_event = self.generate_dummy_auction_event(url_a)
    with patch("sys.stdin") as mock_stdin:
        MaddUrl = Mock()
        Minit = Mock(return_value=None)
        with patch('Foo.crawler.crawlerapp.CrawlerApp.__init__', Minit, create=True):
            with patch('Foo.crawler.crawlerapp.CrawlerApp.addUrl', MaddUrl, create=True):

                ca = Foo.crawler.crawlerapp.CrawlerApp(1)
                mock_stdin.read.return_value = EventDataFrame(1, "TOKEN1", dummy_event.SerializeToString()).to_bytes()
                ca._running = True
                input_thread = threading.Thread(target=ca.inputStreamThread)
                input_thread.start()
                time.sleep(0.5)
                ca._running = False
    MaddUrl.assert_called_with(url_a)

Upvotes: 4

User
User

Reputation: 14853

I would say the following:

@patch.object... #1
def test_check... #2

is equivalent to

def test_check... #2

test_check.object = ... #1

And one can not assign new attributes to function objects.

Upvotes: -1

Related Questions