Unit test speed improvements (#4463)

* Unit test speed improvements

- Move from insantiating data in setUp to setUpTestData

* Update UserMixin class for API testing

* Bunch of test updates

* Further test updates

* Test fixes

* Add allowances for exchange rate server not responding

* Fixes for group role test
This commit is contained in:
Oliver 2023-03-08 15:22:08 +11:00 committed by GitHub
parent 9c594ed52b
commit 2dfea9b825
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 258 additions and 180 deletions

View File

@ -33,56 +33,69 @@ class UserMixin:
# Set list of roles automatically associated with the user # Set list of roles automatically associated with the user
roles = [] roles = []
def setUp(self): @classmethod
"""Setup for all tests.""" def setUpTestData(cls):
super().setUp() """Run setup for all tests in a given class"""
super().setUpTestData()
# Create a user to log in with # Create a user to log in with
self.user = get_user_model().objects.create_user( cls.user = get_user_model().objects.create_user(
username=self.username, username=cls.username,
password=self.password, password=cls.password,
email=self.email email=cls.email
) )
# Create a group for the user # Create a group for the user
self.group = Group.objects.create(name='my_test_group') cls.group = Group.objects.create(name='my_test_group')
self.user.groups.add(self.group) cls.user.groups.add(cls.group)
if self.superuser: if cls.superuser:
self.user.is_superuser = True cls.user.is_superuser = True
if self.is_staff: if cls.is_staff:
self.user.is_staff = True cls.user.is_staff = True
self.user.save() cls.user.save()
# Assign all roles if set # Assign all roles if set
if self.roles == 'all': if cls.roles == 'all':
self.assignRole(assign_all=True) cls.assignRole(group=cls.group, assign_all=True)
# else filter the roles # else filter the roles
else: else:
for role in self.roles: for role in cls.roles:
self.assignRole(role) cls.assignRole(role=role, group=cls.group)
def setUp(self):
"""Run setup for individual test methods"""
if self.auto_login: if self.auto_login:
self.client.login(username=self.username, password=self.password) self.client.login(username=self.username, password=self.password)
def assignRole(self, role=None, assign_all: bool = False): @classmethod
def assignRole(cls, role=None, assign_all: bool = False, group=None):
"""Set the user roles for the registered user. """Set the user roles for the registered user.
Arguments: Arguments:
role: Role of the format 'rule.permission' e.g. 'part.add' role: Role of the format 'rule.permission' e.g. 'part.add'
assign_all: Set to True to assign *all* roles assign_all: Set to True to assign *all* roles
group: The group to assign roles to (or leave None to use the group assigned to this class)
""" """
if group is None:
group = cls.group
if type(assign_all) is not bool: if type(assign_all) is not bool:
# Raise exception if common mistake is made! # Raise exception if common mistake is made!
raise TypeError('assign_all must be a boolean value') raise TypeError('assignRole: assign_all must be a boolean value')
if not role and not assign_all:
raise ValueError('assignRole: either role must be provided, or assign_all must be set')
if not assign_all and role: if not assign_all and role:
rule, perm = role.split('.') rule, perm = role.split('.')
for ruleset in self.group.rule_sets.all(): for ruleset in group.rule_sets.all():
if assign_all or ruleset.name == rule: if assign_all or ruleset.name == rule:

View File

@ -8,7 +8,7 @@ from rest_framework import status
from InvenTree.api_tester import InvenTreeAPITestCase from InvenTree.api_tester import InvenTreeAPITestCase
from InvenTree.helpers import InvenTreeTestCase from InvenTree.helpers import InvenTreeTestCase
from users.models import RuleSet from users.models import RuleSet, update_group_roles
class HTMLAPITests(InvenTreeTestCase): class HTMLAPITests(InvenTreeTestCase):
@ -126,6 +126,10 @@ class APITests(InvenTreeAPITestCase):
""" """
url = reverse('api-user-roles') url = reverse('api-user-roles')
# Delete all rules
self.group.rule_sets.all().delete()
update_group_roles(self.group)
response = self.client.get(url, format='json') response = self.client.get(url, format='json')
# Not logged in, so cannot access user role data # Not logged in, so cannot access user role data

View File

@ -390,10 +390,10 @@ class TestMPTT(TestCase):
'location', 'location',
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Setup for all tests.""" """Setup for all tests."""
super().setUp() super().setUpTestData()
StockLocation.objects.rebuild() StockLocation.objects.rebuild()
def test_self_as_parent(self): def test_self_as_parent(self):

View File

@ -731,38 +731,42 @@ class BuildOverallocationTest(BuildAPITest):
Using same Build ID=1 as allocation test above. Using same Build ID=1 as allocation test above.
""" """
def setUp(self): @classmethod
def setUpTestData(cls):
"""Basic operation as part of test suite setup""" """Basic operation as part of test suite setup"""
super().setUp() super().setUpTestData()
self.assignRole('build.add') cls.assignRole('build.add')
self.assignRole('build.change') cls.assignRole('build.change')
self.build = Build.objects.get(pk=1) cls.build = Build.objects.get(pk=1)
self.url = reverse('api-build-finish', kwargs={'pk': self.build.pk}) cls.url = reverse('api-build-finish', kwargs={'pk': cls.build.pk})
StockItem.objects.create(part=Part.objects.get(pk=50), quantity=30) StockItem.objects.create(part=Part.objects.get(pk=50), quantity=30)
# Keep some state for use in later assertions, and then overallocate # Keep some state for use in later assertions, and then overallocate
self.state = {} cls.state = {}
self.allocation = {} cls.allocation = {}
for i, bi in enumerate(self.build.part.bom_items.all()):
rq = self.build.required_quantity(bi, None) + i + 1 for i, bi in enumerate(cls.build.part.bom_items.all()):
rq = cls.build.required_quantity(bi, None) + i + 1
si = StockItem.objects.filter(part=bi.sub_part, quantity__gte=rq).first() si = StockItem.objects.filter(part=bi.sub_part, quantity__gte=rq).first()
self.state[bi.sub_part] = (si, si.quantity, rq) cls.state[bi.sub_part] = (si, si.quantity, rq)
BuildItem.objects.create( BuildItem.objects.create(
build=self.build, build=cls.build,
stock_item=si, stock_item=si,
quantity=rq, quantity=rq,
) )
# create and complete outputs # create and complete outputs
self.build.create_build_output(self.build.quantity) cls.build.create_build_output(cls.build.quantity)
outputs = self.build.build_outputs.all() outputs = cls.build.build_outputs.all()
self.build.complete_build_output(outputs[0], self.user) cls.build.complete_build_output(outputs[0], cls.user)
def test_setup(self):
"""Validate expected state after set-up."""
# Validate expected state after set-up.
self.assertEqual(self.build.incomplete_outputs.count(), 0) self.assertEqual(self.build.incomplete_outputs.count(), 0)
self.assertEqual(self.build.complete_outputs.count(), 1) self.assertEqual(self.build.complete_outputs.count(), 1)
self.assertEqual(self.build.completed, self.build.quantity) self.assertEqual(self.build.completed, self.build.quantity)

View File

@ -29,7 +29,8 @@ class BuildTestBase(TestCase):
'users', 'users',
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Initialize data to use for these tests. """Initialize data to use for these tests.
The base Part 'assembly' has a BOM consisting of three parts: The base Part 'assembly' has a BOM consisting of three parts:
@ -45,27 +46,29 @@ class BuildTestBase(TestCase):
""" """
super().setUpTestData()
# Create a base "Part" # Create a base "Part"
self.assembly = Part.objects.create( cls.assembly = Part.objects.create(
name="An assembled part", name="An assembled part",
description="Why does it matter what my description is?", description="Why does it matter what my description is?",
assembly=True, assembly=True,
trackable=True, trackable=True,
) )
self.sub_part_1 = Part.objects.create( cls.sub_part_1 = Part.objects.create(
name="Widget A", name="Widget A",
description="A widget", description="A widget",
component=True component=True
) )
self.sub_part_2 = Part.objects.create( cls.sub_part_2 = Part.objects.create(
name="Widget B", name="Widget B",
description="A widget", description="A widget",
component=True component=True
) )
self.sub_part_3 = Part.objects.create( cls.sub_part_3 = Part.objects.create(
name="Widget C", name="Widget C",
description="A widget", description="A widget",
component=True, component=True,
@ -73,63 +76,63 @@ class BuildTestBase(TestCase):
) )
# Create BOM item links for the parts # Create BOM item links for the parts
self.bom_item_1 = BomItem.objects.create( cls.bom_item_1 = BomItem.objects.create(
part=self.assembly, part=cls.assembly,
sub_part=self.sub_part_1, sub_part=cls.sub_part_1,
quantity=5 quantity=5
) )
self.bom_item_2 = BomItem.objects.create( cls.bom_item_2 = BomItem.objects.create(
part=self.assembly, part=cls.assembly,
sub_part=self.sub_part_2, sub_part=cls.sub_part_2,
quantity=3, quantity=3,
optional=True optional=True
) )
# sub_part_3 is trackable! # sub_part_3 is trackable!
self.bom_item_3 = BomItem.objects.create( cls.bom_item_3 = BomItem.objects.create(
part=self.assembly, part=cls.assembly,
sub_part=self.sub_part_3, sub_part=cls.sub_part_3,
quantity=2 quantity=2
) )
ref = generate_next_build_reference() ref = generate_next_build_reference()
# Create a "Build" object to make 10x objects # Create a "Build" object to make 10x objects
self.build = Build.objects.create( cls.build = Build.objects.create(
reference=ref, reference=ref,
title="This is a build", title="This is a build",
part=self.assembly, part=cls.assembly,
quantity=10, quantity=10,
issued_by=get_user_model().objects.get(pk=1), issued_by=get_user_model().objects.get(pk=1),
) )
# Create some build output (StockItem) objects # Create some build output (StockItem) objects
self.output_1 = StockItem.objects.create( cls.output_1 = StockItem.objects.create(
part=self.assembly, part=cls.assembly,
quantity=3, quantity=3,
is_building=True, is_building=True,
build=self.build build=cls.build
) )
self.output_2 = StockItem.objects.create( cls.output_2 = StockItem.objects.create(
part=self.assembly, part=cls.assembly,
quantity=7, quantity=7,
is_building=True, is_building=True,
build=self.build, build=cls.build,
) )
# Create some stock items to assign to the build # Create some stock items to assign to the build
self.stock_1_1 = StockItem.objects.create(part=self.sub_part_1, quantity=3) cls.stock_1_1 = StockItem.objects.create(part=cls.sub_part_1, quantity=3)
self.stock_1_2 = StockItem.objects.create(part=self.sub_part_1, quantity=100) cls.stock_1_2 = StockItem.objects.create(part=cls.sub_part_1, quantity=100)
self.stock_2_1 = StockItem.objects.create(part=self.sub_part_2, quantity=5) cls.stock_2_1 = StockItem.objects.create(part=cls.sub_part_2, quantity=5)
self.stock_2_2 = StockItem.objects.create(part=self.sub_part_2, quantity=5) cls.stock_2_2 = StockItem.objects.create(part=cls.sub_part_2, quantity=5)
self.stock_2_3 = StockItem.objects.create(part=self.sub_part_2, quantity=5) cls.stock_2_3 = StockItem.objects.create(part=cls.sub_part_2, quantity=5)
self.stock_2_4 = StockItem.objects.create(part=self.sub_part_2, quantity=5) cls.stock_2_4 = StockItem.objects.create(part=cls.sub_part_2, quantity=5)
self.stock_2_5 = StockItem.objects.create(part=self.sub_part_2, quantity=5) cls.stock_2_5 = StockItem.objects.create(part=cls.sub_part_2, quantity=5)
self.stock_3_1 = StockItem.objects.create(part=self.sub_part_3, quantity=1000) cls.stock_3_1 = StockItem.objects.create(part=cls.sub_part_3, quantity=1000)
class BuildTest(BuildTestBase): class BuildTest(BuildTestBase):

View File

@ -1,6 +1,7 @@
"""Tests for mechanisms in common.""" """Tests for mechanisms in common."""
import json import json
import time
from datetime import timedelta from datetime import timedelta
from http import HTTPStatus from http import HTTPStatus
@ -921,7 +922,16 @@ class CurrencyAPITests(InvenTreeAPITestCase):
# Delete any existing exchange rate data # Delete any existing exchange rate data
Rate.objects.all().delete() Rate.objects.all().delete()
self.post(reverse('api-currency-refresh')) # Updating via the external exchange may not work every time
for _idx in range(5):
self.post(reverse('api-currency-refresh'))
# There should be some new exchange rate objects now # There should be some new exchange rate objects now
self.assertTrue(Rate.objects.all().exists()) if Rate.objects.all().exists():
# Exit early
return
# Delay and try again
time.sleep(10)
raise TimeoutError("Could not refresh currency exchange data after 5 attempts")

View File

@ -17,11 +17,14 @@ class CompanyTest(InvenTreeAPITestCase):
'purchase_order.change', 'purchase_order.change',
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Perform initialization for the unit test class""" """Perform initialization for the unit test class"""
super().setUp()
self.acme = Company.objects.create(name='ACME', description='Supplier', is_customer=False, is_supplier=True) super().setUpTestData()
# Create some company objects to work with
cls.acme = Company.objects.create(name='ACME', description='Supplier', is_customer=False, is_supplier=True)
Company.objects.create(name='Drippy Cup Co.', description='Customer', is_customer=True, is_supplier=False) Company.objects.create(name='Drippy Cup Co.', description='Customer', is_customer=True, is_supplier=False)
Company.objects.create(name='Sippy Cup Emporium', description='Another supplier') Company.objects.create(name='Sippy Cup Emporium', description='Another supplier')

View File

@ -26,8 +26,12 @@ class CompanySimpleTest(TestCase):
'price_breaks', 'price_breaks',
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Perform initialization for the tests in this class""" """Perform initialization for the tests in this class"""
super().setUpTestData()
Company.objects.create(name='ABC Co.', Company.objects.create(name='ABC Co.',
description='Seller of ABC products', description='Seller of ABC products',
website='www.abc-sales.com', website='www.abc-sales.com',
@ -35,10 +39,10 @@ class CompanySimpleTest(TestCase):
is_customer=False, is_customer=False,
is_supplier=True) is_supplier=True)
self.acme0001 = SupplierPart.objects.get(SKU='ACME0001') cls.acme0001 = SupplierPart.objects.get(SKU='ACME0001')
self.acme0002 = SupplierPart.objects.get(SKU='ACME0002') cls.acme0002 = SupplierPart.objects.get(SKU='ACME0002')
self.zerglphs = SupplierPart.objects.get(SKU='ZERGLPHS') cls.zerglphs = SupplierPart.objects.get(SKU='ZERGLPHS')
self.zergm312 = SupplierPart.objects.get(SKU='ZERGM312') cls.zergm312 = SupplierPart.objects.get(SKU='ZERGM312')
def test_company_model(self): def test_company_model(self):
"""Tests for the company model data""" """Tests for the company model data"""

View File

@ -27,9 +27,10 @@ class LabelTest(InvenTreeAPITestCase):
'stock' 'stock'
] ]
def setUp(self) -> None: @classmethod
def setUpTestData(cls):
"""Ensure that some label instances exist as part of init routine""" """Ensure that some label instances exist as part of init routine"""
super().setUp() super().setUpTestData()
apps.get_app_config('label').create_labels() apps.get_app_config('label').create_labels()
def test_default_labels(self): def test_default_labels(self):

View File

@ -1408,25 +1408,33 @@ class SalesOrderLineItemTest(OrderTest):
LIST_URL = reverse('api-so-line-list') LIST_URL = reverse('api-so-line-list')
def setUp(self): @classmethod
def setUpTestData(cls):
"""Init routine for this unit test class""" """Init routine for this unit test class"""
super().setUp() super().setUpTestData()
# List of salable parts # List of salable parts
parts = Part.objects.filter(salable=True) parts = Part.objects.filter(salable=True)
lines = []
# Create a bunch of SalesOrderLineItems for each order # Create a bunch of SalesOrderLineItems for each order
for idx, so in enumerate(models.SalesOrder.objects.all()): for idx, so in enumerate(models.SalesOrder.objects.all()):
for part in parts: for part in parts:
models.SalesOrderLineItem.objects.create( lines.append(
order=so, models.SalesOrderLineItem(
part=part, order=so,
quantity=(idx + 1) * 5, part=part,
reference=f"Order {so.reference} - line {idx}", quantity=(idx + 1) * 5,
reference=f"Order {so.reference} - line {idx}",
)
) )
self.url = reverse('api-so-line-list') # Bulk create
models.SalesOrderLineItem.objects.bulk_create(lines)
cls.url = reverse('api-so-line-list')
def test_so_line_list(self): def test_so_line_list(self):
"""Test list endpoint""" """Test list endpoint"""

View File

@ -25,33 +25,34 @@ class SalesOrderTest(TestCase):
'users', 'users',
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Initial setup for this set of unit tests""" """Initial setup for this set of unit tests"""
# Create a Company to ship the goods to # Create a Company to ship the goods to
self.customer = Company.objects.create(name="ABC Co", description="My customer", is_customer=True) cls.customer = Company.objects.create(name="ABC Co", description="My customer", is_customer=True)
# Create a Part to ship # Create a Part to ship
self.part = Part.objects.create(name='Spanner', salable=True, description='A spanner that I sell') cls.part = Part.objects.create(name='Spanner', salable=True, description='A spanner that I sell')
# Create some stock! # Create some stock!
self.Sa = StockItem.objects.create(part=self.part, quantity=100) cls.Sa = StockItem.objects.create(part=cls.part, quantity=100)
self.Sb = StockItem.objects.create(part=self.part, quantity=200) cls.Sb = StockItem.objects.create(part=cls.part, quantity=200)
# Create a SalesOrder to ship against # Create a SalesOrder to ship against
self.order = SalesOrder.objects.create( cls.order = SalesOrder.objects.create(
customer=self.customer, customer=cls.customer,
reference='SO-1234', reference='SO-1234',
customer_reference='ABC 55555' customer_reference='ABC 55555'
) )
# Create a Shipment against this SalesOrder # Create a Shipment against this SalesOrder
self.shipment = SalesOrderShipment.objects.create( cls.shipment = SalesOrderShipment.objects.create(
order=self.order, order=cls.order,
reference='SO-001', reference='SO-001',
) )
# Create a line item # Create a line item
self.line = SalesOrderLineItem.objects.create(quantity=50, order=self.order, part=self.part) cls.line = SalesOrderLineItem.objects.create(quantity=50, order=cls.order, part=cls.part)
def test_so_reference(self): def test_so_reference(self):
"""Unit tests for sales order generation""" """Unit tests for sales order generation"""

View File

@ -2357,12 +2357,12 @@ class PartPricing(common.models.MetaMixin):
if self.scheduled_for_update: if self.scheduled_for_update:
# Ignore if the pricing is already scheduled to be updated # Ignore if the pricing is already scheduled to be updated
logger.info(f"Pricing for {p} already scheduled for update - skipping") logger.debug(f"Pricing for {p} already scheduled for update - skipping")
return return
if counter > 25: if counter > 25:
# Prevent infinite recursion / stack depth issues # Prevent infinite recursion / stack depth issues
logger.info(counter, f"Skipping pricing update for {p} - maximum depth exceeded") logger.debug(counter, f"Skipping pricing update for {p} - maximum depth exceeded")
return return
try: try:

View File

@ -1893,15 +1893,16 @@ class PartAPIAggregationTest(InvenTreeAPITestCase):
'part.change', 'part.change',
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Create test data as part of setup routine""" """Create test data as part of setup routine"""
super().setUp() super().setUpTestData()
# Ensure the part "variant" tree is correctly structured # Ensure the part "variant" tree is correctly structured
Part.objects.rebuild() Part.objects.rebuild()
# Add a new part # Add a new part
self.part = Part.objects.create( cls.part = Part.objects.create(
name='Banana', name='Banana',
description='This is a banana', description='This is a banana',
category=PartCategory.objects.get(pk=1), category=PartCategory.objects.get(pk=1),
@ -1910,12 +1911,12 @@ class PartAPIAggregationTest(InvenTreeAPITestCase):
# Create some stock items associated with the part # Create some stock items associated with the part
# First create 600 units which are OK # First create 600 units which are OK
StockItem.objects.create(part=self.part, quantity=100) StockItem.objects.create(part=cls.part, quantity=100)
StockItem.objects.create(part=self.part, quantity=200) StockItem.objects.create(part=cls.part, quantity=200)
StockItem.objects.create(part=self.part, quantity=300) StockItem.objects.create(part=cls.part, quantity=300)
# Now create another 400 units which are LOST # Now create another 400 units which are LOST
StockItem.objects.create(part=self.part, quantity=400, status=StockStatus.LOST) StockItem.objects.create(part=cls.part, quantity=400, status=StockStatus.LOST)
def get_part_data(self): def get_part_data(self):
"""Helper function for retrieving part data""" """Helper function for retrieving part data"""

View File

@ -17,26 +17,35 @@ class BomUploadTest(InvenTreeAPITestCase):
'part.change', 'part.change',
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Create BOM data as part of setup routine""" """Create BOM data as part of setup routine"""
super().setUp() super().setUpTestData()
self.part = Part.objects.create( cls.part = Part.objects.create(
name='Assembly', name='Assembly',
description='An assembled part', description='An assembled part',
assembly=True, assembly=True,
component=False, component=False,
) )
parts = []
for i in range(10): for i in range(10):
Part.objects.create( parts.append(
name=f"Component {i}", Part(
IPN=f"CMP_{i}", name=f"Component {i}",
description="A subcomponent that can be used in a BOM", IPN=f"CMP_{i}",
component=True, description="A subcomponent that can be used in a BOM",
assembly=False, component=True,
assembly=False,
lft=0, rght=0,
level=0, tree_id=0,
)
) )
Part.objects.bulk_create(parts)
def post_bom(self, filename, file_data, clear_existing=None, expected_code=None, content_type='text/plain'): def post_bom(self, filename, file_data, clear_existing=None, expected_code=None, content_type='text/plain'):
"""Helper function for submitting a BOM file""" """Helper function for submitting a BOM file"""
bom_file = SimpleUploadedFile( bom_file = SimpleUploadedFile(

View File

@ -19,15 +19,19 @@ class CategoryTest(TestCase):
'params', 'params',
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Extract some interesting categories for time-saving""" """Extract some interesting categories for time-saving"""
self.electronics = PartCategory.objects.get(name='Electronics')
self.mechanical = PartCategory.objects.get(name='Mechanical') super().setUpTestData()
self.resistors = PartCategory.objects.get(name='Resistors')
self.capacitors = PartCategory.objects.get(name='Capacitors') cls.electronics = PartCategory.objects.get(name='Electronics')
self.fasteners = PartCategory.objects.get(name='Fasteners') cls.mechanical = PartCategory.objects.get(name='Mechanical')
self.ic = PartCategory.objects.get(name='IC') cls.resistors = PartCategory.objects.get(name='Resistors')
self.transceivers = PartCategory.objects.get(name='Transceivers') cls.capacitors = PartCategory.objects.get(name='Capacitors')
cls.fasteners = PartCategory.objects.get(name='Fasteners')
cls.ic = PartCategory.objects.get(name='IC')
cls.transceivers = PartCategory.objects.get(name='Transceivers')
def test_parents(self): def test_parents(self):
"""Test that the parent fields are properly set, based on the test fixtures.""" """Test that the parent fields are properly set, based on the test fixtures."""

View File

@ -134,14 +134,16 @@ class PartTest(TestCase):
'part_pricebreaks' 'part_pricebreaks'
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Create some Part instances as part of init routine""" """Create some Part instances as part of init routine"""
super().setUp()
self.r1 = Part.objects.get(name='R_2K2_0805') super().setUpTestData()
self.r2 = Part.objects.get(name='R_4K7_0603')
self.c1 = Part.objects.get(name='C_22N_0805') cls.r1 = Part.objects.get(name='R_2K2_0805')
cls.r2 = Part.objects.get(name='R_4K7_0603')
cls.c1 = Part.objects.get(name='C_22N_0805')
Part.objects.rebuild() Part.objects.rebuild()
@ -529,15 +531,16 @@ class PartSubscriptionTests(InvenTreeTestCase):
'part', 'part',
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Create category and part data as part of setup routine""" """Create category and part data as part of setup routine"""
super().setUp() super().setUpTestData()
# electronics / IC / MCU # Electronics / IC / MCU
self.category = PartCategory.objects.get(pk=4) cls.category = PartCategory.objects.get(pk=4)
self.part = Part.objects.create( cls.part = Part.objects.create(
category=self.category, category=cls.category,
name='STM32F103', name='STM32F103',
description='Currently worth a lot of money', description='Currently worth a lot of money',
is_template=True, is_template=True,
@ -629,14 +632,16 @@ class BaseNotificationIntegrationTest(InvenTreeTestCase):
'stock' 'stock'
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Add an email address as part of initialization""" """Add an email address as part of initialization"""
super().setUp() super().setUpTestData()
# Add Mailadress
EmailAddress.objects.create(user=self.user, email='test@testing.com') # Add email address
EmailAddress.objects.create(user=cls.user, email='test@testing.com')
# Define part that will be tested # Define part that will be tested
self.part = Part.objects.get(name='R_2K2_0805') cls.part = Part.objects.get(name='R_2K2_0805')
def _notification_run(self, run_class=None): def _notification_run(self, run_class=None):
"""Run a notification test suit through. """Run a notification test suit through.

View File

@ -20,6 +20,8 @@ class PartPricingTests(InvenTreeTestCase):
def setUp(self): def setUp(self):
"""Setup routines""" """Setup routines"""
super().setUp()
self.generate_exchange_rates() self.generate_exchange_rates()
# Create a new part for performing pricing calculations # Create a new part for performing pricing calculations
@ -29,8 +31,6 @@ class PartPricingTests(InvenTreeTestCase):
assembly=True assembly=True
) )
return super().setUp()
def create_price_breaks(self): def create_price_breaks(self):
"""Create some price breaks for the part, in various currencies""" """Create some price breaks for the part, in various currencies"""

View File

@ -66,25 +66,29 @@ class PluginTagTests(TestCase):
class InvenTreePluginTests(TestCase): class InvenTreePluginTests(TestCase):
"""Tests for InvenTreePlugin.""" """Tests for InvenTreePlugin."""
def setUp(self): @classmethod
def setUpTestData(cls):
"""Setup for all tests.""" """Setup for all tests."""
self.plugin = InvenTreePlugin()
super().setUpTestData()
cls.plugin = InvenTreePlugin()
class NamedPlugin(InvenTreePlugin): class NamedPlugin(InvenTreePlugin):
"""a named plugin.""" """a named plugin."""
NAME = 'abc123' NAME = 'abc123'
self.named_plugin = NamedPlugin() cls.named_plugin = NamedPlugin()
class SimpleInvenTreePlugin(InvenTreePlugin): class SimpleInvenTreePlugin(InvenTreePlugin):
NAME = 'SimplePlugin' NAME = 'SimplePlugin'
self.plugin_simple = SimpleInvenTreePlugin() cls.plugin_simple = SimpleInvenTreePlugin()
class OldInvenTreePlugin(InvenTreePlugin): class OldInvenTreePlugin(InvenTreePlugin):
PLUGIN_SLUG = 'old' PLUGIN_SLUG = 'old'
self.plugin_old = OldInvenTreePlugin() cls.plugin_old = OldInvenTreePlugin()
class NameInvenTreePlugin(InvenTreePlugin): class NameInvenTreePlugin(InvenTreePlugin):
NAME = 'Aplugin' NAME = 'Aplugin'
@ -97,8 +101,8 @@ class InvenTreePluginTests(TestCase):
WEBSITE = 'https://aa.bb/cc' WEBSITE = 'https://aa.bb/cc'
LICENSE = 'MIT' LICENSE = 'MIT'
self.plugin_name = NameInvenTreePlugin() cls.plugin_name = NameInvenTreePlugin()
self.plugin_sample = SampleIntegrationPlugin() cls.plugin_sample = SampleIntegrationPlugin()
class VersionInvenTreePlugin(InvenTreePlugin): class VersionInvenTreePlugin(InvenTreePlugin):
NAME = 'Version' NAME = 'Version'
@ -106,7 +110,7 @@ class InvenTreePluginTests(TestCase):
MIN_VERSION = '0.1.0' MIN_VERSION = '0.1.0'
MAX_VERSION = '0.1.3' MAX_VERSION = '0.1.3'
self.plugin_version = VersionInvenTreePlugin() cls.plugin_version = VersionInvenTreePlugin()
def test_basic_plugin_init(self): def test_basic_plugin_init(self):
"""Check if a basic plugin intis.""" """Check if a basic plugin intis."""

View File

@ -50,9 +50,10 @@ class StockLocationTest(StockAPITestCase):
list_url = reverse('api-location-list') list_url = reverse('api-location-list')
def setUp(self): @classmethod
def setUpTestData(cls):
"""Setup for all tests.""" """Setup for all tests."""
super().setUp() super().setUpTestData()
# Add some stock locations # Add some stock locations
StockLocation.objects.create(name='top', description='top category') StockLocation.objects.create(name='top', description='top category')
@ -1413,30 +1414,32 @@ class StockMergeTest(StockAPITestCase):
URL = reverse('api-stock-merge') URL = reverse('api-stock-merge')
def setUp(self): @classmethod
def setUpTestData(cls):
"""Setup for all tests.""" """Setup for all tests."""
super().setUp()
self.part = part.models.Part.objects.get(pk=25) super().setUpTestData()
self.loc = StockLocation.objects.get(pk=1)
self.sp_1 = company.models.SupplierPart.objects.get(pk=100)
self.sp_2 = company.models.SupplierPart.objects.get(pk=101)
self.item_1 = StockItem.objects.create( cls.part = part.models.Part.objects.get(pk=25)
part=self.part, cls.loc = StockLocation.objects.get(pk=1)
supplier_part=self.sp_1, cls.sp_1 = company.models.SupplierPart.objects.get(pk=100)
cls.sp_2 = company.models.SupplierPart.objects.get(pk=101)
cls.item_1 = StockItem.objects.create(
part=cls.part,
supplier_part=cls.sp_1,
quantity=100, quantity=100,
) )
self.item_2 = StockItem.objects.create( cls.item_2 = StockItem.objects.create(
part=self.part, part=cls.part,
supplier_part=self.sp_2, supplier_part=cls.sp_2,
quantity=100, quantity=100,
) )
self.item_3 = StockItem.objects.create( cls.item_3 = StockItem.objects.create(
part=self.part, part=cls.part,
supplier_part=self.sp_2, supplier_part=cls.sp_2,
quantity=50, quantity=50,
) )

View File

@ -30,19 +30,20 @@ class StockTestBase(InvenTreeTestCase):
'stock_tests', 'stock_tests',
] ]
def setUp(self): @classmethod
def setUpTestData(cls):
"""Setup for all tests.""" """Setup for all tests."""
super().setUp() super().setUpTestData()
# Extract some shortcuts from the fixtures # Extract some shortcuts from the fixtures
self.home = StockLocation.objects.get(name='Home') cls.home = StockLocation.objects.get(name='Home')
self.bathroom = StockLocation.objects.get(name='Bathroom') cls.bathroom = StockLocation.objects.get(name='Bathroom')
self.diningroom = StockLocation.objects.get(name='Dining Room') cls.diningroom = StockLocation.objects.get(name='Dining Room')
self.office = StockLocation.objects.get(name='Office') cls.office = StockLocation.objects.get(name='Office')
self.drawer1 = StockLocation.objects.get(name='Drawer_1') cls.drawer1 = StockLocation.objects.get(name='Drawer_1')
self.drawer2 = StockLocation.objects.get(name='Drawer_2') cls.drawer2 = StockLocation.objects.get(name='Drawer_2')
self.drawer3 = StockLocation.objects.get(name='Drawer_3') cls.drawer3 = StockLocation.objects.get(name='Drawer_3')
# Ensure the MPTT objects are correctly rebuild # Ensure the MPTT objects are correctly rebuild
Part.objects.rebuild() Part.objects.rebuild()