1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Reorganize base TestCase classes

This commit is contained in:
jeremystretch
2021-04-14 14:22:58 -04:00
parent 8701b20899
commit 664ba55460
12 changed files with 222 additions and 212 deletions

View File

@ -6,7 +6,7 @@ from django.urls import reverse
from circuits.choices import *
from circuits.models import *
from dcim.models import Cable, Interface, Site
from utilities.testing import ViewTestCases, create_test_device
from utilities.testing import ViewTestCases, create_tags, create_test_device
class ProviderTestCase(ViewTestCases.PrimaryObjectViewTestCase):
@ -21,7 +21,7 @@ class ProviderTestCase(ViewTestCases.PrimaryObjectViewTestCase):
Provider(name='Provider 3', slug='provider-3', asn=65003),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'name': 'Provider X',
@ -106,7 +106,7 @@ class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
Circuit(cid='Circuit 3', provider=providers[0], type=circuittypes[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'cid': 'Circuit X',
@ -157,7 +157,7 @@ class ProviderNetworkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
ProviderNetwork(name='Provider Network 3', provider=providers[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'name': 'Provider Network X',

View File

@ -12,7 +12,7 @@ from dcim.choices import *
from dcim.constants import *
from dcim.models import *
from ipam.models import VLAN
from utilities.testing import ViewTestCases, create_test_device
from utilities.testing import ViewTestCases, create_tags, create_test_device
class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
@ -109,7 +109,7 @@ class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
Site(name='Site 3', slug='site-3', region=regions[0], group=groups[1]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'name': 'Site X',
@ -242,7 +242,7 @@ class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
RackReservation(rack=rack, user=user2, units=[7, 8, 9], description='Reservation 3'),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'rack': rack.pk,
@ -298,7 +298,7 @@ class RackTestCase(ViewTestCases.PrimaryObjectViewTestCase):
Rack(name='Rack 3', site=sites[0]),
))
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'name': 'Rack X',
@ -413,7 +413,7 @@ class DeviceTypeTestCase(
DeviceType(model='Device Type 3', slug='device-type-3', manufacturer=manufacturers[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'manufacturer': manufacturers[1].pk,
@ -1021,7 +1021,7 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
Device(name='Device 3', site=sites[0], rack=racks[0], device_type=devicetypes[0], device_role=deviceroles[0], platform=platforms[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device_type': devicetypes[1].pk,
@ -1201,7 +1201,7 @@ class ConsolePortTestCase(ViewTestCases.DeviceComponentViewTestCase):
ConsolePort(device=device, name='Console Port 3'),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device': device.pk,
@ -1259,7 +1259,7 @@ class ConsoleServerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
ConsoleServerPort(device=device, name='Console Server Port 3'),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device': device.pk,
@ -1315,7 +1315,7 @@ class PowerPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
PowerPort(device=device, name='Power Port 3'),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device': device.pk,
@ -1383,7 +1383,7 @@ class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase):
PowerOutlet(device=device, name='Power Outlet 3', power_port=powerports[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device': device.pk,
@ -1452,7 +1452,7 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
)
VLAN.objects.bulk_create(vlans)
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device': device.pk,
@ -1539,7 +1539,7 @@ class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
FrontPort(device=device, name='Front Port 3', rear_port=rearports[2]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device': device.pk,
@ -1600,7 +1600,7 @@ class RearPortTestCase(ViewTestCases.DeviceComponentViewTestCase):
RearPort(device=device, name='Rear Port 3'),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device': device.pk,
@ -1661,7 +1661,7 @@ class DeviceBayTestCase(ViewTestCases.DeviceComponentViewTestCase):
DeviceBay(device=device, name='Device Bay 3'),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device': device.pk,
@ -1701,7 +1701,7 @@ class InventoryItemTestCase(ViewTestCases.DeviceComponentViewTestCase):
InventoryItem.objects.create(device=device, name='Inventory Item 2')
InventoryItem.objects.create(device=device, name='Inventory Item 3')
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device': device.pk,
@ -1791,7 +1791,7 @@ class CableTestCase(
Cable(termination_a=interfaces[1], termination_b=interfaces[4], type=CableTypeChoices.TYPE_CAT6).save()
Cable(termination_a=interfaces[2], termination_b=interfaces[5], type=CableTypeChoices.TYPE_CAT6).save()
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
interface_ct = ContentType.objects.get_for_model(Interface)
cls.form_data = {
@ -1918,7 +1918,7 @@ class PowerPanelTestCase(ViewTestCases.PrimaryObjectViewTestCase):
PowerPanel(site=sites[0], location=locations[0], name='Power Panel 3'),
))
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'site': sites[1].pk,
@ -1966,7 +1966,7 @@ class PowerFeedTestCase(ViewTestCases.PrimaryObjectViewTestCase):
PowerFeed(name='Power Feed 3', power_panel=powerpanels[0], rack=racks[0]),
))
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'name': 'Power Feed X',

View File

@ -7,7 +7,7 @@ from dcim.models import Site
from extras.choices import *
from extras.models import CustomField, ObjectChange, Tag
from utilities.testing import APITestCase
from utilities.testing.utils import post_data
from utilities.testing.utils import create_tags, post_data
from utilities.testing.views import ModelViewTestCase
@ -38,7 +38,7 @@ class ChangeLogViewTest(ModelViewTestCase):
cf_select.content_types.set([ct])
def test_create_object(self):
tags = self.create_tags('Tag 1', 'Tag 2')
tags = create_tags('Tag 1', 'Tag 2')
form_data = {
'name': 'Site 1',
'slug': 'site-1',
@ -72,7 +72,7 @@ class ChangeLogViewTest(ModelViewTestCase):
def test_update_object(self):
site = Site(name='Site 1', slug='site-1')
site.save()
tags = self.create_tags('Tag 1', 'Tag 2', 'Tag 3')
tags = create_tags('Tag 1', 'Tag 2', 'Tag 3')
site.tags.set('Tag 1', 'Tag 2')
form_data = {
@ -116,7 +116,7 @@ class ChangeLogViewTest(ModelViewTestCase):
}
)
site.save()
self.create_tags('Tag 1', 'Tag 2')
create_tags('Tag 1', 'Tag 2')
site.tags.set('Tag 1', 'Tag 2')
request = {

View File

@ -2,7 +2,7 @@ from django.urls import reverse
from rest_framework import status
from dcim.models import Site
from utilities.testing import APITestCase
from utilities.testing import APITestCase, create_tags
class TaggedItemTest(APITestCase):
@ -10,7 +10,7 @@ class TaggedItemTest(APITestCase):
Test the application of Tags to and item (a Site, for example) upon creation (POST) and modification (PATCH).
"""
def test_create_tagged_item(self):
tags = self.create_tags("Foo", "Bar", "Baz")
tags = create_tags("Foo", "Bar", "Baz")
data = {
'name': 'Test Site',
'slug': 'test-site',
@ -37,7 +37,7 @@ class TaggedItemTest(APITestCase):
slug='test-site'
)
site.tags.add("Foo", "Bar", "Baz")
self.create_tags("New Tag")
create_tags("New Tag")
data = {
'tags': [
{"name": "Foo"},

View File

@ -6,7 +6,7 @@ from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
from ipam.choices import *
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
from tenancy.models import Tenant
from utilities.testing import ViewTestCases
from utilities.testing import ViewTestCases, create_tags
class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
@ -27,7 +27,7 @@ class VRFTestCase(ViewTestCases.PrimaryObjectViewTestCase):
VRF(name='VRF 3', rd='65000:3'),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'name': 'VRF X',
@ -64,7 +64,7 @@ class RouteTargetTestCase(ViewTestCases.PrimaryObjectViewTestCase):
)
Tenant.objects.bulk_create(tenants)
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
route_targets = (
RouteTarget(name='65000:1001', tenant=tenants[0]),
@ -141,7 +141,7 @@ class AggregateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
Aggregate(prefix=IPNetwork('10.3.0.0/16'), rir=rirs[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'prefix': IPNetwork('10.99.0.0/16'),
@ -226,7 +226,7 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
Prefix(prefix=IPNetwork('10.3.0.0/16'), vrf=vrfs[0], site=sites[0], role=roles[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'prefix': IPNetwork('192.0.2.0/24'),
@ -277,7 +277,7 @@ class IPAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
IPAddress(address=IPNetwork('192.0.2.3/24'), vrf=vrfs[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'vrf': vrfs[1].pk,
@ -374,7 +374,7 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
VLAN(group=vlangroups[0], vid=103, name='VLAN103', site=sites[0], role=roles[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'site': sites[1].pk,
@ -434,7 +434,7 @@ class ServiceTestCase(
Service(device=device, name='Service 3', protocol=ServiceProtocolChoices.PROTOCOL_TCP, ports=[103]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'device': device.pk,

View File

@ -1,5 +1,5 @@
from tenancy.models import Tenant, TenantGroup
from utilities.testing import ViewTestCases
from utilities.testing import ViewTestCases, create_tags
class TenantGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
@ -53,7 +53,7 @@ class TenantTestCase(ViewTestCases.PrimaryObjectViewTestCase):
Tenant(name='Tenant 3', slug='tenant-3', group=tenant_groups[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'name': 'Tenant X',

View File

@ -1,3 +1,4 @@
from .api import *
from .base import *
from .utils import *
from .views import *

View File

@ -9,8 +9,8 @@ from rest_framework.test import APIClient
from extras.choices import ObjectChangeActionChoices
from extras.models import ObjectChange
from users.models import ObjectPermission, Token
from .base import ModelTestCase
from .utils import disable_warnings
from .views import ModelTestCase
__all__ = (

View File

@ -0,0 +1,161 @@
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import FieldDoesNotExist
from django.db.models import ManyToManyField
from django.forms.models import model_to_dict
from django.test import Client, TestCase as _TestCase
from netaddr import IPNetwork
from taggit.managers import TaggableManager
from users.models import ObjectPermission
from utilities.permissions import resolve_permission_ct
from .utils import extract_form_failures
__all__ = (
'ModelTestCase',
'TestCase',
)
class TestCase(_TestCase):
user_permissions = ()
def setUp(self):
# Create the test user and assign permissions
self.user = User.objects.create_user(username='testuser')
self.add_permissions(*self.user_permissions)
# Initialize the test client
self.client = Client()
self.client.force_login(self.user)
#
# Permissions management
#
def add_permissions(self, *names):
"""
Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model>.
"""
for name in names:
ct, action = resolve_permission_ct(name)
obj_perm = ObjectPermission(name=name, actions=[action])
obj_perm.save()
obj_perm.users.add(self.user)
obj_perm.object_types.add(ct)
#
# Custom assertions
#
def assertHttpStatus(self, response, expected_status):
"""
TestCase method. Provide more detail in the event of an unexpected HTTP response.
"""
err_message = None
# Construct an error message only if we know the test is going to fail
if response.status_code != expected_status:
if hasattr(response, 'data'):
# REST API response; pass the response data through directly
err = response.data
else:
# Attempt to extract form validation errors from the response HTML
form_errors = extract_form_failures(response.content)
err = form_errors or response.content or 'No data'
err_message = f"Expected HTTP status {expected_status}; received {response.status_code}: {err}"
self.assertEqual(response.status_code, expected_status, err_message)
class ModelTestCase(TestCase):
"""
Parent class for TestCases which deal with models.
"""
model = None
def _get_queryset(self):
"""
Return a base queryset suitable for use in test methods.
"""
return self.model.objects.all()
def prepare_instance(self, instance):
"""
Test cases can override this method to perform any necessary manipulation of an instance prior to its evaluation
against test data. For example, it can be used to decrypt a Secret's plaintext attribute.
"""
return instance
def model_to_dict(self, instance, fields, api=False):
"""
Return a dictionary representation of an instance.
"""
# Prepare the instance and call Django's model_to_dict() to extract all fields
model_dict = model_to_dict(self.prepare_instance(instance), fields=fields)
# Map any additional (non-field) instance attributes that were specified
for attr in fields:
if hasattr(instance, attr) and attr not in model_dict:
model_dict[attr] = getattr(instance, attr)
for key, value in list(model_dict.items()):
try:
field = instance._meta.get_field(key)
except FieldDoesNotExist:
# Attribute is not a model field
continue
# Handle ManyToManyFields
if value and type(field) in (ManyToManyField, TaggableManager):
if field.related_model is ContentType:
model_dict[key] = sorted([f'{ct.app_label}.{ct.model}' for ct in value])
else:
model_dict[key] = sorted([obj.pk for obj in value])
if api:
# Replace ContentType numeric IDs with <app_label>.<model>
if type(getattr(instance, key)) is ContentType:
ct = ContentType.objects.get(pk=value)
model_dict[key] = f'{ct.app_label}.{ct.model}'
# Convert IPNetwork instances to strings
elif type(value) is IPNetwork:
model_dict[key] = str(value)
else:
# Convert ArrayFields to CSV strings
if type(instance._meta.get_field(key)) is ArrayField:
model_dict[key] = ','.join([str(v) for v in value])
return model_dict
#
# Custom assertions
#
def assertInstanceEqual(self, instance, data, exclude=None, api=False):
"""
Compare a model instance to a dictionary, checking that its attribute values match those specified
in the dictionary.
:param instance: Python object instance
:param data: Dictionary of test data used to define the instance
:param exclude: List of fields to exclude from comparison (e.g. passwords, which get hashed)
:param api: Set to True is the data is a JSON representation of the instance
"""
if exclude is None:
exclude = []
fields = [k for k in data.keys() if k not in exclude]
model_dict = self.model_to_dict(instance, fields=fields, api=api)
# Omit any dictionary keys which are not instance attributes or have been excluded
relevant_data = {
k: v for k, v in data.items() if hasattr(instance, k) and k not in exclude
}
self.assertDictEqual(model_dict, relevant_data)

View File

@ -3,8 +3,10 @@ import re
from contextlib import contextmanager
from django.contrib.auth.models import Permission, User
from django.utils.text import slugify
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
from extras.models import Tag
def post_data(data):
@ -59,6 +61,15 @@ def create_test_user(username='testuser', permissions=None):
return user
def create_tags(*names):
"""
Create and return a Tag instance for each name given.
"""
tags = [Tag(name=name, slug=slugify(name)) for name in names]
Tag.objects.bulk_create(tags)
return tags
def extract_form_failures(content):
"""
Given raw HTML content from an HTTP response, return a list of form errors.

View File

@ -1,182 +1,20 @@
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
from django.db.models import ManyToManyField
from django.forms.models import model_to_dict
from django.test import Client, TestCase as _TestCase, override_settings
from django.core.exceptions import ObjectDoesNotExist
from django.test import override_settings
from django.urls import reverse
from django.utils.text import slugify
from netaddr import IPNetwork
from taggit.managers import TaggableManager
from extras.choices import ObjectChangeActionChoices
from extras.models import ObjectChange, Tag
from extras.models import ObjectChange
from users.models import ObjectPermission
from utilities.permissions import resolve_permission_ct
from .utils import disable_warnings, extract_form_failures, post_data
from .base import ModelTestCase
from .utils import disable_warnings, post_data
__all__ = (
'TestCase',
'ModelTestCase',
'ModelViewTestCase',
'ViewTestCases',
)
class TestCase(_TestCase):
user_permissions = ()
def setUp(self):
# Create the test user and assign permissions
self.user = User.objects.create_user(username='testuser')
self.add_permissions(*self.user_permissions)
# Initialize the test client
self.client = Client()
self.client.force_login(self.user)
def prepare_instance(self, instance):
"""
Test cases can override this method to perform any necessary manipulation of an instance prior to its evaluation
against test data. For example, it can be used to decrypt a Secret's plaintext attribute.
"""
return instance
def model_to_dict(self, instance, fields, api=False):
"""
Return a dictionary representation of an instance.
"""
# Prepare the instance and call Django's model_to_dict() to extract all fields
model_dict = model_to_dict(self.prepare_instance(instance), fields=fields)
# Map any additional (non-field) instance attributes that were specified
for attr in fields:
if hasattr(instance, attr) and attr not in model_dict:
model_dict[attr] = getattr(instance, attr)
for key, value in list(model_dict.items()):
try:
field = instance._meta.get_field(key)
except FieldDoesNotExist:
# Attribute is not a model field
continue
# Handle ManyToManyFields
if value and type(field) in (ManyToManyField, TaggableManager):
if field.related_model is ContentType:
model_dict[key] = sorted([f'{ct.app_label}.{ct.model}' for ct in value])
else:
model_dict[key] = sorted([obj.pk for obj in value])
if api:
# Replace ContentType numeric IDs with <app_label>.<model>
if type(getattr(instance, key)) is ContentType:
ct = ContentType.objects.get(pk=value)
model_dict[key] = f'{ct.app_label}.{ct.model}'
# Convert IPNetwork instances to strings
elif type(value) is IPNetwork:
model_dict[key] = str(value)
else:
# Convert ArrayFields to CSV strings
if type(instance._meta.get_field(key)) is ArrayField:
model_dict[key] = ','.join([str(v) for v in value])
return model_dict
#
# Permissions management
#
def add_permissions(self, *names):
"""
Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model>.
"""
for name in names:
ct, action = resolve_permission_ct(name)
obj_perm = ObjectPermission(name=name, actions=[action])
obj_perm.save()
obj_perm.users.add(self.user)
obj_perm.object_types.add(ct)
#
# Custom assertions
#
def assertHttpStatus(self, response, expected_status):
"""
TestCase method. Provide more detail in the event of an unexpected HTTP response.
"""
err_message = None
# Construct an error message only if we know the test is going to fail
if response.status_code != expected_status:
if hasattr(response, 'data'):
# REST API response; pass the response data through directly
err = response.data
else:
# Attempt to extract form validation errors from the response HTML
form_errors = extract_form_failures(response.content)
err = form_errors or response.content or 'No data'
err_message = f"Expected HTTP status {expected_status}; received {response.status_code}: {err}"
self.assertEqual(response.status_code, expected_status, err_message)
def assertInstanceEqual(self, instance, data, exclude=None, api=False):
"""
Compare a model instance to a dictionary, checking that its attribute values match those specified
in the dictionary.
:param instance: Python object instance
:param data: Dictionary of test data used to define the instance
:param exclude: List of fields to exclude from comparison (e.g. passwords, which get hashed)
:param api: Set to True is the data is a JSON representation of the instance
"""
if exclude is None:
exclude = []
fields = [k for k in data.keys() if k not in exclude]
model_dict = self.model_to_dict(instance, fields=fields, api=api)
# Omit any dictionary keys which are not instance attributes or have been excluded
relevant_data = {
k: v for k, v in data.items() if hasattr(instance, k) and k not in exclude
}
self.assertDictEqual(model_dict, relevant_data)
#
# Convenience methods
#
@classmethod
def create_tags(cls, *names):
"""
Create and return a Tag instance for each name given.
"""
tags = [Tag(name=name, slug=slugify(name)) for name in names]
Tag.objects.bulk_create(tags)
return tags
class ModelTestCase(TestCase):
"""
Parent class for TestCases which deal with models.
"""
model = None
def _get_queryset(self):
"""
Return a base queryset suitable for use in test methods.
"""
return self.model.objects.all()
#
# UI Tests
#
@ -185,7 +23,6 @@ class ModelViewTestCase(ModelTestCase):
"""
Base TestCase for model views. Subclass to test individual views.
"""
def _get_base_url(self):
"""
Return the base format for a URL for the test's model. Override this to test for a model which belongs

View File

@ -5,7 +5,7 @@ from netaddr import EUI
from dcim.choices import InterfaceModeChoices
from dcim.models import DeviceRole, Platform, Site
from ipam.models import VLAN
from utilities.testing import ViewTestCases
from utilities.testing import ViewTestCases, create_tags
from virtualization.choices import *
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
@ -100,7 +100,7 @@ class ClusterTestCase(ViewTestCases.PrimaryObjectViewTestCase):
Cluster(name='Cluster 3', group=clustergroups[0], type=clustertypes[0], site=sites[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'name': 'Cluster X',
@ -174,7 +174,7 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase):
VirtualMachine(name='Virtual Machine 3', cluster=clusters[0], role=deviceroles[0], platform=platforms[0]),
])
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'cluster': clusters[1].pk,
@ -256,7 +256,7 @@ class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
)
VLAN.objects.bulk_create(vlans)
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'virtual_machine': virtualmachines[1].pk,