I am trying to mock a few calls to s3 in my python code and am running into the some kind of an error when my unittest runs. Below is the code where I invoke the boto module to reach S3. I am trying to mock out all calls to S3 in this code.
from boto.s3.connection import S3Connection
s3_conn = S3Connection()
bucket_obj = s3_conn.get_bucket(bucket)
key = bucket_obj.lookup(path)
return int(key.size)
In my unit test code, I have:
mock_conn = mock.Mock()
mock_bucket = mock.Mock()
mock_key = mock.Mock()
with mock.patch('boto.s3.connection.S3Connection',
mock.Mock(return_value=mock_conn)):
with mock.patch('boto.s3.connection.S3Connection.get_bucket',
mock.Mock(return_value=mock_bucket)):
with mock.patch('boto.s3.connection.S3Connection.get_bucket.lookup',
mock.Mock(return_value=mock_key)):
mock_key.size.return_value = 50000
self.assertEquals(fake.check_size(),
50)
However, I run into this error when I run my unittest:
Traceback (most recent call last):
File "/home/vagrant/code/my-project/tests/test_loadfirmware.py", line 92, in test_check_size
self.assertEquals(fake.check_size(),
File "/home/vagrant/code/my-project/test_s3.py", line 80, in check_size
bucket_obj = s3_conn.get_bucket(bucket)
File "/home/vagrant/code/my-project/venv/local/lib/python2.7/site-packages/boto/s3/connection.py", line 506, in get_bucket
return self.head_bucket(bucket_name, headers=headers)
File "/home/vagrant/code/my-project/venv/local/lib/python2.7/site-packages/boto/s3/connection.py", line 525, in head_bucket
response = self.make_request('HEAD', bucket_name, headers=headers)
File "/home/vagrant/code/my-project/venv/local/lib/python2.7/site-packages/boto/s3/connection.py", line 664, in make_request
return super(S3Connection, self).make_request(
TypeError: must be type, not Mock
Any idea what I am missing here? I would assume that the mocking would be straightforward like I have in my code?
The problem is that you are calling the real boto in your test case, not your mock.
You need to mock the object using the same symbol name it has inside your test module. Instead of trying to patch 'boto.s3.connection.S3Connection', you want to patch 'test_s3.S3Connection'. It starts with 'test_s3' because that is the name of the module from where you are loading the code under test. Then, after the dot, use the same symbol that inside that module gives you access to S3Connection (that is, without boto.s3.connection).
Also, patch creates a mock automatically for you, so you do not need to create the first mock object. And as you already patched S3Connection, instead of patching things inside it, you just need to plug your created mocks as return values for the proper methods:
mock_bucket = mock.Mock()
mock_key = mock.Mock()
with mock.patch('test_s3.S3Connection') as mock_conn:
mock_conn.get_bucket.return_value = mock_bucket
mock_bucket.lookup.return_value = mock_key
mock_key.size.return_value = 50000
self.assertEquals(fake.check_size(), 50)