Use fakes instead of mocks for data objects

We hit a nasty infinite recursion issue because we were passing mock's
to obj_to_dict which was setting the mock's attributes as attributes of
the returned Bunch objects. Really though, we should just not be turning
mock objects into Bunch's.

Change-Id: I91a69a87082b30e16545c45ef8705ef4e929d5ca
This commit is contained in:
Gregory Haynes 2015-05-11 22:59:09 +00:00
parent a89dbfadf0
commit 3328cc77da
4 changed files with 138 additions and 127 deletions

59
shade/tests/fakes.py Normal file
View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.V
"""
fakes
----------------------------------
Fakes used for testing
"""
class FakeFlavor(object):
def __init__(self, id, name):
self.id = id
self.name = name
class FakeImage(object):
def __init__(self, id, name, status):
self.id = id
self.name = name
self.status = status
class FakeProject(object):
def __init__(self, id):
self.id = id
class FakeServer(object):
def __init__(self, id, name, status):
self.id = id
self.name = name
self.status = status
class FakeUser(object):
def __init__(self, id, email, name):
self.id = id
self.email = email
self.name = name
class FakeVolume(object):
def __init__(self, id, status, display_name):
self.id = id
self.status = status
self.display_name = display_name

View File

@ -19,6 +19,7 @@ import yaml
import shade import shade
from shade import meta from shade import meta
from shade.tests import fakes
from shade.tests.unit import base from shade.tests.unit import base
@ -67,82 +68,68 @@ class TestMemoryCache(base.TestCase):
@mock.patch('shade.OpenStackCloud.keystone_client') @mock.patch('shade.OpenStackCloud.keystone_client')
def test_project_cache(self, keystone_mock): def test_project_cache(self, keystone_mock):
mock_project = mock.MagicMock() project = fakes.FakeProject('project_a')
mock_project.id = 'project_a' keystone_mock.projects.list.return_value = [project]
keystone_mock.projects.list.return_value = [mock_project] self.assertEqual({'project_a': project}, self.cloud.project_cache)
self.assertEqual({'project_a': mock_project}, self.cloud.project_cache) project_b = fakes.FakeProject('project_b')
mock_project_b = mock.MagicMock() keystone_mock.projects.list.return_value = [project,
mock_project_b.id = 'project_b' project_b]
keystone_mock.projects.list.return_value = [mock_project,
mock_project_b]
self.assertEqual( self.assertEqual(
{'project_a': mock_project}, self.cloud.project_cache) {'project_a': project}, self.cloud.project_cache)
self.cloud.get_project_cache.invalidate(self.cloud) self.cloud.get_project_cache.invalidate(self.cloud)
self.assertEqual( self.assertEqual(
{'project_a': mock_project, {'project_a': project,
'project_b': mock_project_b}, self.cloud.project_cache) 'project_b': project_b}, self.cloud.project_cache)
@mock.patch('shade.OpenStackCloud.cinder_client') @mock.patch('shade.OpenStackCloud.cinder_client')
def test_list_volumes(self, cinder_mock): def test_list_volumes(self, cinder_mock):
mock_volume = mock.MagicMock() fake_volume = fakes.FakeVolume('volume1', 'available',
mock_volume.id = 'volume1' 'Volume 1 Display Name')
mock_volume.status = 'available' fake_volume_dict = meta.obj_to_dict(fake_volume)
mock_volume.display_name = 'Volume 1 Display Name' cinder_mock.volumes.list.return_value = [fake_volume]
mock_volume_dict = meta.obj_to_dict(mock_volume) self.assertEqual([fake_volume_dict], self.cloud.list_volumes())
cinder_mock.volumes.list.return_value = [mock_volume] fake_volume2 = fakes.FakeVolume('volume2', 'available',
self.assertEqual([mock_volume_dict], self.cloud.list_volumes()) 'Volume 2 Display Name')
mock_volume2 = mock.MagicMock() fake_volume2_dict = meta.obj_to_dict(fake_volume2)
mock_volume2.id = 'volume2' cinder_mock.volumes.list.return_value = [fake_volume, fake_volume2]
mock_volume2.status = 'available' self.assertEqual([fake_volume_dict], self.cloud.list_volumes())
mock_volume2.display_name = 'Volume 2 Display Name'
mock_volume2_dict = meta.obj_to_dict(mock_volume2)
cinder_mock.volumes.list.return_value = [mock_volume, mock_volume2]
self.assertEqual([mock_volume_dict], self.cloud.list_volumes())
self.cloud.list_volumes.invalidate(self.cloud) self.cloud.list_volumes.invalidate(self.cloud)
self.assertEqual([mock_volume_dict, mock_volume2_dict], self.assertEqual([fake_volume_dict, fake_volume2_dict],
self.cloud.list_volumes()) self.cloud.list_volumes())
@mock.patch('shade.OpenStackCloud.cinder_client') @mock.patch('shade.OpenStackCloud.cinder_client')
def test_list_volumes_creating_invalidates(self, cinder_mock): def test_list_volumes_creating_invalidates(self, cinder_mock):
mock_volume = mock.MagicMock() fake_volume = fakes.FakeVolume('volume1', 'creating',
mock_volume.id = 'volume1' 'Volume 1 Display Name')
mock_volume.status = 'creating' fake_volume_dict = meta.obj_to_dict(fake_volume)
mock_volume.display_name = 'Volume 1 Display Name' cinder_mock.volumes.list.return_value = [fake_volume]
mock_volume_dict = meta.obj_to_dict(mock_volume) self.assertEqual([fake_volume_dict], self.cloud.list_volumes())
cinder_mock.volumes.list.return_value = [mock_volume] fake_volume2 = fakes.FakeVolume('volume2', 'available',
self.assertEqual([mock_volume_dict], self.cloud.list_volumes()) 'Volume 2 Display Name')
mock_volume2 = mock.MagicMock() fake_volume2_dict = meta.obj_to_dict(fake_volume2)
mock_volume2.id = 'volume2' cinder_mock.volumes.list.return_value = [fake_volume, fake_volume2]
mock_volume2.status = 'available' self.assertEqual([fake_volume_dict, fake_volume2_dict],
mock_volume2.display_name = 'Volume 2 Display Name'
mock_volume2_dict = meta.obj_to_dict(mock_volume2)
cinder_mock.volumes.list.return_value = [mock_volume, mock_volume2]
self.assertEqual([mock_volume_dict, mock_volume2_dict],
self.cloud.list_volumes()) self.cloud.list_volumes())
@mock.patch.object(shade.OpenStackCloud, 'cinder_client') @mock.patch.object(shade.OpenStackCloud, 'cinder_client')
def test_create_volume_invalidates(self, cinder_mock): def test_create_volume_invalidates(self, cinder_mock):
mock_volb4 = mock.MagicMock() fake_volb4 = fakes.FakeVolume('volume1', 'available',
mock_volb4.id = 'volume1' 'Volume 1 Display Name')
mock_volb4.status = 'available' fake_volb4_dict = meta.obj_to_dict(fake_volb4)
mock_volb4.display_name = 'Volume 1 Display Name' cinder_mock.volumes.list.return_value = [fake_volb4]
mock_volb4_dict = meta.obj_to_dict(mock_volb4) self.assertEqual([fake_volb4_dict], self.cloud.list_volumes())
cinder_mock.volumes.list.return_value = [mock_volb4]
self.assertEqual([mock_volb4_dict], self.cloud.list_volumes())
volume = dict(display_name='junk_vol', volume = dict(display_name='junk_vol',
size=1, size=1,
display_description='test junk volume') display_description='test junk volume')
mock_vol = mock.Mock() fake_vol = fakes.FakeVolume('12345', 'creating', '')
mock_vol.status = 'creating' fake_vol_dict = meta.obj_to_dict(fake_vol)
mock_vol.id = '12345' cinder_mock.volumes.create.return_value = fake_vol
mock_vol_dict = meta.obj_to_dict(mock_vol) cinder_mock.volumes.list.return_value = [fake_volb4, fake_vol]
cinder_mock.volumes.create.return_value = mock_vol
cinder_mock.volumes.list.return_value = [mock_volb4, mock_vol]
def creating_available(): def creating_available():
def now_available(): def now_available():
mock_vol.status = 'available' fake_vol.status = 'available'
mock_vol_dict['status'] = 'available' fake_vol_dict['status'] = 'available'
return mock.DEFAULT return mock.DEFAULT
cinder_mock.volumes.list.side_effect = now_available cinder_mock.volumes.list.side_effect = now_available
return mock.DEFAULT return mock.DEFAULT
@ -153,40 +140,36 @@ class TestMemoryCache(base.TestCase):
# If cache was not invalidated, we would not see our own volume here # If cache was not invalidated, we would not see our own volume here
# because the first volume was available and thus would already be # because the first volume was available and thus would already be
# cached. # cached.
self.assertEqual([mock_volb4_dict, mock_vol_dict], self.assertEqual([fake_volb4_dict, fake_vol_dict],
self.cloud.list_volumes()) self.cloud.list_volumes())
# And now delete and check same thing since list is cached as all # And now delete and check same thing since list is cached as all
# available # available
mock_vol.status = 'deleting' fake_vol.status = 'deleting'
mock_vol_dict = meta.obj_to_dict(mock_vol) fake_vol_dict = meta.obj_to_dict(fake_vol)
def deleting_gone(): def deleting_gone():
def now_gone(): def now_gone():
cinder_mock.volumes.list.return_value = [mock_volb4] cinder_mock.volumes.list.return_value = [fake_volb4]
return mock.DEFAULT return mock.DEFAULT
cinder_mock.volumes.list.side_effect = now_gone cinder_mock.volumes.list.side_effect = now_gone
return mock.DEFAULT return mock.DEFAULT
cinder_mock.volumes.list.return_value = [mock_volb4, mock_vol] cinder_mock.volumes.list.return_value = [fake_volb4, fake_vol]
cinder_mock.volumes.list.side_effect = deleting_gone cinder_mock.volumes.list.side_effect = deleting_gone
cinder_mock.volumes.delete.return_value = mock_vol_dict cinder_mock.volumes.delete.return_value = fake_vol_dict
self.cloud.delete_volume('12345') self.cloud.delete_volume('12345')
self.assertEqual([mock_volb4_dict], self.cloud.list_volumes()) self.assertEqual([fake_volb4_dict], self.cloud.list_volumes())
@mock.patch.object(shade.OpenStackCloud, 'keystone_client') @mock.patch.object(shade.OpenStackCloud, 'keystone_client')
def test_get_user_cache(self, keystone_mock): def test_get_user_cache(self, keystone_mock):
mock_user = mock.MagicMock() fake_user = fakes.FakeUser('999', '', '')
mock_user.id = '999' keystone_mock.users.list.return_value = [fake_user]
keystone_mock.users.list.return_value = [mock_user] self.assertEqual({'999': fake_user}, self.cloud.get_user_cache())
self.assertEqual({'999': mock_user}, self.cloud.get_user_cache())
@mock.patch.object(shade.OpenStackCloud, 'keystone_client') @mock.patch.object(shade.OpenStackCloud, 'keystone_client')
def test_modify_user_invalidates_cache(self, keystone_mock): def test_modify_user_invalidates_cache(self, keystone_mock):
class User(object): fake_user = fakes.FakeUser('abc123', 'abc123@domain.test',
id = 'abc123' 'abc123 name')
email = 'abc123@domain.test'
name = 'abc123 name'
fake_user = User()
# first cache an empty list # first cache an empty list
keystone_mock.users.list.return_value = [] keystone_mock.users.list.return_value = []
self.assertEqual({}, self.cloud.get_user_cache()) self.assertEqual({}, self.cloud.get_user_cache())
@ -201,8 +184,8 @@ class TestMemoryCache(base.TestCase):
# Cache should have been invalidated # Cache should have been invalidated
self.assertEqual({'abc123': fake_user}, self.cloud.get_user_cache()) self.assertEqual({'abc123': fake_user}, self.cloud.get_user_cache())
# Update and check to see if it is updated # Update and check to see if it is updated
fake_user2 = User() fake_user2 = fakes.FakeUser('abc123', 'abc123 name',
fake_user2.email = 'abc123-changed@domain.test' 'abc123-changed@domain.test')
keystone_mock.users.update.return_value = fake_user2 keystone_mock.users.update.return_value = fake_user2
keystone_mock.users.list.return_value = [fake_user2] keystone_mock.users.list.return_value = [fake_user2]
self.cloud.update_user('abc123', email='abc123-changed@domain.test') self.cloud.update_user('abc123', email='abc123-changed@domain.test')
@ -220,10 +203,7 @@ class TestMemoryCache(base.TestCase):
nova_mock.flavors.list.return_value = [] nova_mock.flavors.list.return_value = []
self.assertEqual([], self.cloud.list_flavors()) self.assertEqual([], self.cloud.list_flavors())
class Flavor(object): fake_flavor = fakes.FakeFlavor('555', 'vanilla')
id = '555'
name = 'vanilla'
fake_flavor = Flavor()
fake_flavor_dict = meta.obj_to_dict(fake_flavor) fake_flavor_dict = meta.obj_to_dict(fake_flavor)
nova_mock.flavors.list.return_value = [fake_flavor] nova_mock.flavors.list.return_value = [fake_flavor]
self.cloud.list_flavors.invalidate(self.cloud) self.cloud.list_flavors.invalidate(self.cloud)
@ -234,11 +214,7 @@ class TestMemoryCache(base.TestCase):
glance_mock.images.list.return_value = [] glance_mock.images.list.return_value = []
self.assertEqual([], self.cloud.list_images()) self.assertEqual([], self.cloud.list_images())
class Image(object): fake_image = fakes.FakeImage('22', '22 name', 'success')
id = '22'
name = '22 name'
status = 'success'
fake_image = Image()
fake_image_dict = meta.obj_to_dict(fake_image) fake_image_dict = meta.obj_to_dict(fake_image)
glance_mock.images.list.return_value = [fake_image] glance_mock.images.list.return_value = [fake_image]
self.cloud.list_images.invalidate(self.cloud) self.cloud.list_images.invalidate(self.cloud)
@ -246,20 +222,11 @@ class TestMemoryCache(base.TestCase):
@mock.patch.object(shade.OpenStackCloud, 'glance_client') @mock.patch.object(shade.OpenStackCloud, 'glance_client')
def test_list_images_ignores_unsteady_status(self, glance_mock): def test_list_images_ignores_unsteady_status(self, glance_mock):
class Image(object): steady_image = fakes.FakeImage('68', 'Jagr', 'active')
id = None
name = None
status = None
steady_image = Image()
steady_image.id = '68'
steady_image.name = 'Jagr'
steady_image.status = 'active'
steady_image_dict = meta.obj_to_dict(steady_image) steady_image_dict = meta.obj_to_dict(steady_image)
for status in ('queued', 'saving', 'pending_delete'): for status in ('queued', 'saving', 'pending_delete'):
active_image = Image() active_image = fakes.FakeImage(self.getUniqueString(),
active_image.id = self.getUniqueString() self.getUniqueString(), status)
active_image.name = self.getUniqueString()
active_image.status = status
glance_mock.images.list.return_value = [active_image] glance_mock.images.list.return_value = [active_image]
active_image_dict = meta.obj_to_dict(active_image) active_image_dict = meta.obj_to_dict(active_image)
self.assertEqual([active_image_dict], self.assertEqual([active_image_dict],
@ -271,20 +238,11 @@ class TestMemoryCache(base.TestCase):
@mock.patch.object(shade.OpenStackCloud, 'glance_client') @mock.patch.object(shade.OpenStackCloud, 'glance_client')
def test_list_images_caches_steady_status(self, glance_mock): def test_list_images_caches_steady_status(self, glance_mock):
class Image(object): steady_image = fakes.FakeImage('91', 'Federov', 'active')
id = None
name = None
status = None
steady_image = Image()
steady_image.id = '91'
steady_image.name = 'Federov'
steady_image.status = 'active'
first_image = None first_image = None
for status in ('active', 'deleted', 'killed'): for status in ('active', 'deleted', 'killed'):
active_image = Image() active_image = fakes.FakeImage(self.getUniqueString(),
active_image.id = self.getUniqueString() self.getUniqueString(), status)
active_image.name = self.getUniqueString()
active_image.status = status
active_image_dict = meta.obj_to_dict(active_image) active_image_dict = meta.obj_to_dict(active_image)
if not first_image: if not first_image:
first_image = active_image_dict first_image = active_image_dict
@ -309,11 +267,7 @@ class TestMemoryCache(base.TestCase):
glance_mock.images.list.return_value = [] glance_mock.images.list.return_value = []
self.assertEqual([], self.cloud.list_images()) self.assertEqual([], self.cloud.list_images())
class Image(object): fake_image = fakes.FakeImage('42', '42 name', 'success')
id = '42'
name = '42 name'
status = 'success'
fake_image = Image()
glance_mock.images.create.return_value = fake_image glance_mock.images.create.return_value = fake_image
glance_mock.images.list.return_value = [fake_image] glance_mock.images.list.return_value = [fake_image]
self._call_create_image('42 name') self._call_create_image('42 name')

View File

@ -22,7 +22,7 @@ Tests for the `create_server` command.
from mock import patch, Mock from mock import patch, Mock
from shade import OpenStackCloud from shade import OpenStackCloud
from shade.exc import (OpenStackCloudException, OpenStackCloudTimeout) from shade.exc import (OpenStackCloudException, OpenStackCloudTimeout)
from shade.tests import base from shade.tests import base, fakes
class TestCreateServer(base.TestCase): class TestCreateServer(base.TestCase):
@ -109,14 +109,14 @@ class TestCreateServer(base.TestCase):
novaclient create call returns the server instance. novaclient create call returns the server instance.
""" """
with patch("shade.OpenStackCloud"): with patch("shade.OpenStackCloud"):
mock_server = Mock(status="BUILD") fake_server = fakes.FakeServer('', '', 'BUILD')
config = { config = {
"servers.create.return_value": mock_server, "servers.create.return_value": fake_server,
"servers.get.return_value": mock_server "servers.get.return_value": fake_server
} }
OpenStackCloud.nova_client = Mock(**config) OpenStackCloud.nova_client = Mock(**config)
self.assertEqual( self.assertEqual(
self.client.create_server(), mock_server) self.client.create_server(), fake_server)
def test_create_server_wait(self): def test_create_server_wait(self):
""" """
@ -124,15 +124,16 @@ class TestCreateServer(base.TestCase):
its status changes to "ACTIVE". its status changes to "ACTIVE".
""" """
with patch("shade.OpenStackCloud"): with patch("shade.OpenStackCloud"):
mock_server = Mock(status="ACTIVE") fake_server = fakes.FakeServer('', '', 'ACTIVE')
config = { config = {
"servers.create.return_value": Mock(status="BUILD"), "servers.create.return_value": fakes.FakeServer('', '',
'ACTIVE'),
"servers.get.side_effect": [ "servers.get.side_effect": [
Mock(status="BUILD"), mock_server] Mock(status="BUILD"), fake_server]
} }
OpenStackCloud.nova_client = Mock(**config) OpenStackCloud.nova_client = Mock(**config)
with patch.object(OpenStackCloud, "add_ips_to_server", with patch.object(OpenStackCloud, "add_ips_to_server",
return_value=mock_server): return_value=fake_server):
self.assertEqual( self.assertEqual(
self.client.create_server(wait=True), self.client.create_server(wait=True),
mock_server) fake_server)

View File

@ -23,6 +23,7 @@ import mock
from novaclient import exceptions as nova_exc from novaclient import exceptions as nova_exc
from shade import OpenStackCloud from shade import OpenStackCloud
from shade.tests import fakes
from shade.tests.unit import base from shade.tests.unit import base
@ -37,9 +38,7 @@ class TestDeleteServer(base.TestCase):
""" """
Test that novaclient server delete is called when wait=False Test that novaclient server delete is called when wait=False
""" """
server = mock.MagicMock(id='1234', server = fakes.FakeServer('1234', 'daffy', 'ACTIVE')
status='ACTIVE')
server.name = 'daffy'
nova_mock.servers.list.return_value = [server] nova_mock.servers.list.return_value = [server]
self.cloud.delete_server('daffy', wait=False) self.cloud.delete_server('daffy', wait=False)
nova_mock.servers.delete.assert_called_with(server=server.id) nova_mock.servers.delete.assert_called_with(server=server.id)
@ -63,9 +62,7 @@ class TestDeleteServer(base.TestCase):
""" """
Test that delete_server waits for NotFound from novaclient Test that delete_server waits for NotFound from novaclient
""" """
server = mock.MagicMock(id='9999', server = fakes.FakeServer('9999', 'wily', 'ACTIVE')
status='ACTIVE')
server.name = 'wily'
nova_mock.servers.list.return_value = [server] nova_mock.servers.list.return_value = [server]
def _delete_wily(*args, **kwargs): def _delete_wily(*args, **kwargs):