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
roles = []
def setUp(self):
"""Setup for all tests."""
super().setUp()
@classmethod
def setUpTestData(cls):
"""Run setup for all tests in a given class"""
super().setUpTestData()
# Create a user to log in with
self.user = get_user_model().objects.create_user(
username=self.username,
password=self.password,
email=self.email
cls.user = get_user_model().objects.create_user(
username=cls.username,
password=cls.password,
email=cls.email
)
# Create a group for the user
self.group = Group.objects.create(name='my_test_group')
self.user.groups.add(self.group)
cls.group = Group.objects.create(name='my_test_group')
cls.user.groups.add(cls.group)
if self.superuser:
self.user.is_superuser = True
if cls.superuser:
cls.user.is_superuser = True
if self.is_staff:
self.user.is_staff = True
if cls.is_staff:
cls.user.is_staff = True
self.user.save()
cls.user.save()
# Assign all roles if set
if self.roles == 'all':
self.assignRole(assign_all=True)
if cls.roles == 'all':
cls.assignRole(group=cls.group, assign_all=True)
# else filter the roles
else:
for role in self.roles:
self.assignRole(role)
for role in cls.roles:
cls.assignRole(role=role, group=cls.group)
def setUp(self):
"""Run setup for individual test methods"""
if self.auto_login:
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.
Arguments:
role: Role of the format 'rule.permission' e.g. 'part.add'
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:
# 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:
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:

View File

@ -8,7 +8,7 @@ from rest_framework import status
from InvenTree.api_tester import InvenTreeAPITestCase
from InvenTree.helpers import InvenTreeTestCase
from users.models import RuleSet
from users.models import RuleSet, update_group_roles
class HTMLAPITests(InvenTreeTestCase):
@ -126,6 +126,10 @@ class APITests(InvenTreeAPITestCase):
"""
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')
# Not logged in, so cannot access user role data

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
"""Tests for mechanisms in common."""
import json
import time
from datetime import timedelta
from http import HTTPStatus
@ -921,7 +922,16 @@ class CurrencyAPITests(InvenTreeAPITestCase):
# Delete any existing exchange rate data
Rate.objects.all().delete()
# 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
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',
]
def setUp(self):
@classmethod
def setUpTestData(cls):
"""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='Sippy Cup Emporium', description='Another supplier')

View File

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

View File

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

View File

@ -1408,25 +1408,33 @@ class SalesOrderLineItemTest(OrderTest):
LIST_URL = reverse('api-so-line-list')
def setUp(self):
@classmethod
def setUpTestData(cls):
"""Init routine for this unit test class"""
super().setUp()
super().setUpTestData()
# List of salable parts
parts = Part.objects.filter(salable=True)
lines = []
# Create a bunch of SalesOrderLineItems for each order
for idx, so in enumerate(models.SalesOrder.objects.all()):
for part in parts:
models.SalesOrderLineItem.objects.create(
lines.append(
models.SalesOrderLineItem(
order=so,
part=part,
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):
"""Test list endpoint"""

View File

@ -25,33 +25,34 @@ class SalesOrderTest(TestCase):
'users',
]
def setUp(self):
@classmethod
def setUpTestData(cls):
"""Initial setup for this set of unit tests"""
# 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
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!
self.Sa = StockItem.objects.create(part=self.part, quantity=100)
self.Sb = StockItem.objects.create(part=self.part, quantity=200)
cls.Sa = StockItem.objects.create(part=cls.part, quantity=100)
cls.Sb = StockItem.objects.create(part=cls.part, quantity=200)
# Create a SalesOrder to ship against
self.order = SalesOrder.objects.create(
customer=self.customer,
cls.order = SalesOrder.objects.create(
customer=cls.customer,
reference='SO-1234',
customer_reference='ABC 55555'
)
# Create a Shipment against this SalesOrder
self.shipment = SalesOrderShipment.objects.create(
order=self.order,
cls.shipment = SalesOrderShipment.objects.create(
order=cls.order,
reference='SO-001',
)
# 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):
"""Unit tests for sales order generation"""

View File

@ -2357,12 +2357,12 @@ class PartPricing(common.models.MetaMixin):
if self.scheduled_for_update:
# 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
if counter > 25:
# 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
try:

View File

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

View File

@ -17,25 +17,34 @@ class BomUploadTest(InvenTreeAPITestCase):
'part.change',
]
def setUp(self):
@classmethod
def setUpTestData(cls):
"""Create BOM data as part of setup routine"""
super().setUp()
super().setUpTestData()
self.part = Part.objects.create(
cls.part = Part.objects.create(
name='Assembly',
description='An assembled part',
assembly=True,
component=False,
)
parts = []
for i in range(10):
Part.objects.create(
parts.append(
Part(
name=f"Component {i}",
IPN=f"CMP_{i}",
description="A subcomponent that can be used in a BOM",
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'):
"""Helper function for submitting a BOM file"""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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