diff --git a/app/code/Magento/Bundle/Test/Fixture/Link.php b/app/code/Magento/Bundle/Test/Fixture/Link.php
new file mode 100644
index 0000000000000..eae4a81034753
--- /dev/null
+++ b/app/code/Magento/Bundle/Test/Fixture/Link.php
@@ -0,0 +1,72 @@
+ null,
+ 'sku' => null,
+ 'option_id' => null,
+ 'qty' => 1,
+ 'position' => 1,
+ 'is_default' => false,
+ 'price' => null,
+ 'price_type' => null,
+ 'can_change_quantity' => 0
+ ];
+
+ /**
+ * @var ProcessorInterface
+ */
+ private $dataProcessor;
+
+ /**
+ * @var DataObjectFactory
+ */
+ private $dataObjectFactory;
+
+ /**
+ * @param ProcessorInterface $dataProcessor
+ * @param DataObjectFactory $dataObjectFactory
+ */
+ public function __construct(
+ ProcessorInterface $dataProcessor,
+ DataObjectFactory $dataObjectFactory
+ ) {
+ $this->dataProcessor = $dataProcessor;
+ $this->dataObjectFactory = $dataObjectFactory;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters. Same format as Link::DEFAULT_DATA.
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ return $this->dataObjectFactory->create(['data' => $this->prepareData($data)]);
+ }
+
+ /**
+ * Prepare link data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $data = array_merge(self::DEFAULT_DATA, $data);
+
+ return $this->dataProcessor->process($this, $data);
+ }
+}
diff --git a/app/code/Magento/Bundle/Test/Fixture/Option.php b/app/code/Magento/Bundle/Test/Fixture/Option.php
new file mode 100644
index 0000000000000..8575db1ebe18d
--- /dev/null
+++ b/app/code/Magento/Bundle/Test/Fixture/Option.php
@@ -0,0 +1,113 @@
+ null,
+ 'title' => 'option%uniqid%',
+ 'required' => true,
+ 'type' => 'select',
+ 'position' => 1,
+ 'sku' => null,
+ 'product_links' => []
+ ];
+
+ /**
+ * @var ProcessorInterface
+ */
+ private $dataProcessor;
+
+ /**
+ * @var DataObjectFactory
+ */
+ private $dataObjectFactory;
+
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
+ /**
+ * @param ProcessorInterface $dataProcessor
+ * @param DataObjectFactory $dataObjectFactory
+ */
+ public function __construct(
+ ProcessorInterface $dataProcessor,
+ DataObjectFactory $dataObjectFactory,
+ ProductRepositoryInterface $productRepository
+ ) {
+ $this->dataProcessor = $dataProcessor;
+ $this->dataObjectFactory = $dataObjectFactory;
+ $this->productRepository = $productRepository;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters. Same format as Option::DEFAULT_DATA.
+ * - $data['product_links']: An array of product IDs, SKUs or instances. For advanced configuration use an array
+ * like Link::DEFAULT_DATA.
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ return $this->dataObjectFactory->create(['data' => $this->prepareData($data)]);
+ }
+
+ /**
+ * Prepare option data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $data = array_merge(self::DEFAULT_DATA, $data);
+ $data['product_links'] = $this->prepareLinksData($data);
+
+ return $this->dataProcessor->process($this, $data);
+ }
+
+ /**
+ * Prepare links data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareLinksData(array $data): array
+ {
+ $links = [];
+
+ foreach ($data['product_links'] as $link) {
+ $linkData = [];
+ if (is_numeric($link)) {
+ $product = $this->productRepository->getById($link);
+ $linkData['sku'] = $product->getSku();
+ } elseif (is_string($link)) {
+ $linkData['sku'] = $link;
+ } elseif ($link instanceof ProductInterface) {
+ $product = $this->productRepository->get($link->getSku());
+ $linkData['sku'] = $product->getSku();
+ } else {
+ $linkData = $link instanceof DataObject ? $link->toArray() : $link;
+ }
+
+ $linkData += Link::DEFAULT_DATA;
+ $links[] = $linkData;
+ }
+
+ return $links;
+ }
+}
diff --git a/app/code/Magento/Bundle/Test/Fixture/Product.php b/app/code/Magento/Bundle/Test/Fixture/Product.php
new file mode 100644
index 0000000000000..dd88f263c536c
--- /dev/null
+++ b/app/code/Magento/Bundle/Test/Fixture/Product.php
@@ -0,0 +1,70 @@
+ null,
+ 'type_id' => Type::TYPE_BUNDLE,
+ 'attribute_set_id' => 4,
+ 'name' => 'Bundle Product%uniqid%',
+ 'sku' => 'bundle-product%uniqid%',
+ 'price' => null,
+ 'weight' => null,
+ 'custom_attributes' => [
+ 'price_view' => '0',
+ 'sku_type' => '0',
+ 'price_type' => '0',
+ 'weight_type' => '0',
+ 'shipment_type' => '0',
+ ],
+ 'extension_attributes' => [
+ 'bundle_product_options' => [],
+ ]
+ ];
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters. Same format as \Magento\Catalog\Test\Fixture\Product::DEFAULT_DATA.
+ * Custom attributes and extension attributes can be passed directly in the outer array instead of custom_attributes
+ * or extension_attributes.
+ * Additional fields:
+ * - $data['_options']: An array of options. See Magento\Bundle\Test\Fixture\Option
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ return parent::apply($this->prepareData($data));
+ }
+
+ /**
+ * Prepare product data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $data = array_merge(self::DEFAULT_DATA, $data);
+
+ if (isset($data['_options'])) {
+ $data['extension_attributes']['bundle_product_options'] = array_map(
+ static function ($option) {
+ return $option instanceof DataObject ? $option->toArray() : $option;
+ },
+ $data['_options']
+ );
+ unset($data['_options']);
+ }
+
+ return $data;
+ }
+}
diff --git a/app/code/Magento/Catalog/Test/Fixture/Attribute.php b/app/code/Magento/Catalog/Test/Fixture/Attribute.php
new file mode 100644
index 0000000000000..1f68eb2b832d3
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Fixture/Attribute.php
@@ -0,0 +1,173 @@
+ false,
+ 'is_html_allowed_on_front' => true,
+ 'used_for_sort_by' => false,
+ 'is_filterable' => false,
+ 'is_filterable_in_search' => false,
+ 'is_used_in_grid' => true,
+ 'is_visible_in_grid' => true,
+ 'is_filterable_in_grid' => true,
+ 'position' => 0,
+ 'apply_to' => [],
+ 'is_searchable' => '0',
+ 'is_visible_in_advanced_search' => '0',
+ 'is_comparable' => '0',
+ 'is_used_for_promo_rules' => '0',
+ 'is_visible_on_front' => '0',
+ 'used_in_product_listing' => '0',
+ 'is_visible' => true,
+ 'scope' => 'store',
+ 'attribute_code' => 'product_attribute%uniqid%',
+ 'frontend_input' => 'text',
+ 'entity_type_id' => '4',
+ 'is_required' => false,
+ 'options' => [],
+ 'is_user_defined' => true,
+ 'default_frontend_label' => 'Product Attribute%uniqid%',
+ 'frontend_labels' => [],
+ 'backend_type' => 'varchar',
+ 'is_unique' => '0',
+ 'validation_rules' => []
+
+ ];
+
+ private const DEFAULT_ATTRIBUTE_SET_DATA = [
+ '_set_id' => null,
+ '_group_id' => null,
+ '_sort_order' => 0,
+ ];
+
+ /**
+ * @var ServiceFactory
+ */
+ private $serviceFactory;
+
+ /**
+ * @var ProcessorInterface
+ */
+ private $dataProcessor;
+
+ /**
+ * @var EavSetup
+ */
+ private $eavSetup;
+
+ /**
+ * @var ProductAttributeManagementInterface
+ */
+ private $productAttributeManagement;
+
+ /**
+ * @param ServiceFactory $serviceFactory
+ * @param ProcessorInterface $dataProcessor
+ * @param EavSetup $eavSetup
+ */
+ public function __construct(
+ ServiceFactory $serviceFactory,
+ ProcessorInterface $dataProcessor,
+ EavSetup $eavSetup,
+ ProductAttributeManagementInterface $productAttributeManagement
+ ) {
+ $this->serviceFactory = $serviceFactory;
+ $this->dataProcessor = $dataProcessor;
+ $this->eavSetup = $eavSetup;
+ $this->productAttributeManagement = $productAttributeManagement;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters. Same format as Attribute::DEFAULT_DATA.
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ $service = $this->serviceFactory->create(ProductAttributeRepositoryInterface::class, 'save');
+
+ /**
+ * @var ProductAttributeInterface $attribute
+ */
+ $attribute = $service->execute(
+ [
+ 'attribute' => $this->prepareData(array_diff_key($data, self::DEFAULT_ATTRIBUTE_SET_DATA))
+ ]
+ );
+
+ $attributeSetData = $this->prepareAttributeSetData(
+ array_intersect_key($data, self::DEFAULT_ATTRIBUTE_SET_DATA)
+ );
+
+ $this->productAttributeManagement->assign(
+ $attributeSetData['_set_id'],
+ $attributeSetData['_group_id'],
+ $attribute->getAttributeCode(),
+ $attributeSetData['_sort_order']
+ );
+
+ return $attribute;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function revert(DataObject $data): void
+ {
+ $service = $this->serviceFactory->create(ProductAttributeRepositoryInterface::class, 'deleteById');
+ $service->execute(
+ [
+ 'attributeCode' => $data->getAttributeCode()
+ ]
+ );
+ }
+
+ /**
+ * Prepare attribute data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $data = array_merge(self::DEFAULT_DATA, $data);
+
+ return $this->dataProcessor->process($this, $data);
+ }
+
+ /**
+ * Prepare attribute set data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareAttributeSetData(array $data): array
+ {
+ $attributeSetId = $this->eavSetup->getAttributeSetId(Product::ENTITY, 'Default');
+ $attributeGroupId = $this->eavSetup->getDefaultAttributeGroupId(Product::ENTITY, $attributeSetId);
+ $attributeSetData = [
+ '_set_id' => $attributeSetId,
+ '_group_id' => $attributeGroupId,
+ ];
+ $data = array_merge(self::DEFAULT_ATTRIBUTE_SET_DATA, $attributeSetData, $data);
+
+ return array_intersect_key($data, self::DEFAULT_ATTRIBUTE_SET_DATA);
+ }
+}
diff --git a/app/code/Magento/Catalog/Test/Fixture/Category.php b/app/code/Magento/Catalog/Test/Fixture/Category.php
new file mode 100644
index 0000000000000..4e3a2d67c3aea
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Fixture/Category.php
@@ -0,0 +1,107 @@
+ null,
+ 'name' => 'Category%uniqid%',
+ 'parent_id' => 2,
+ 'is_active' => true,
+ 'position' => 1,
+ 'level' => 1,
+ 'path' => null,
+ 'include_in_menu' => true,
+ 'available_sort_by' => [],
+ 'custom_attributes' => [
+ 'default_sort_by' => ['name']
+ ],
+ 'extension_attributes' => [],
+ 'created_at' => null,
+ 'updated_at' => null,
+ ];
+
+ /**
+ * @var ServiceFactory
+ */
+ private $serviceFactory;
+
+ /**
+ * @var ProcessorInterface
+ */
+ private $dataProcessor;
+
+ /**
+ * @var DataMerger
+ */
+ private $dataMerger;
+
+ /**
+ * @param ServiceFactory $serviceFactory
+ * @param ProcessorInterface $dataProcessor
+ */
+ public function __construct(
+ ServiceFactory $serviceFactory,
+ ProcessorInterface $dataProcessor,
+ DataMerger $dataMerger
+ ) {
+ $this->serviceFactory = $serviceFactory;
+ $this->dataProcessor = $dataProcessor;
+ $this->dataMerger = $dataMerger;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters. Same format as Category::DEFAULT_DATA. Custom attributes and extension attributes
+ * can be passed directly in the outer array instead of custom_attributes or extension_attributes.
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ $service = $this->serviceFactory->create(CategoryRepositoryInterface::class, 'save');
+
+ return $service->execute(
+ [
+ 'category' => $this->prepareData($data)
+ ]
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function revert(DataObject $data): void
+ {
+ $service = $this->serviceFactory->create(CategoryRepositoryInterface::class, 'deleteByIdentifier');
+ $service->execute(
+ [
+ 'categoryId' => $data->getId()
+ ]
+ );
+ }
+
+ /**
+ * Prepare category data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $data = $this->dataMerger->merge(self::DEFAULT_DATA, $data);
+
+ return $this->dataProcessor->process($this, $data);
+ }
+}
diff --git a/app/code/Magento/Catalog/Test/Fixture/Product.php b/app/code/Magento/Catalog/Test/Fixture/Product.php
new file mode 100644
index 0000000000000..3528afaf63423
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Fixture/Product.php
@@ -0,0 +1,127 @@
+ null,
+ 'type_id' => Type::TYPE_SIMPLE,
+ 'attribute_set_id' => 4,
+ 'name' => 'Simple Product%uniqid%',
+ 'sku' => 'simple-product%uniqid%',
+ 'price' => 10,
+ 'weight' => 1,
+ 'visibility' => Visibility::VISIBILITY_BOTH,
+ 'status' => Status::STATUS_ENABLED,
+ 'custom_attributes' => [
+ 'tax_class_id' => '2'
+ ],
+ 'extension_attributes' => [
+ 'website_ids' => [1],
+ 'category_links' => [],
+ 'stock_item' => [
+ 'use_config_manage_stock' => true,
+ 'qty' => 100,
+ 'is_qty_decimal' => false,
+ 'is_in_stock' => true,
+ ]
+ ],
+ 'product_links' => [],
+ 'options' => [],
+ 'media_gallery_entries' => [],
+ 'tier_prices' => [],
+ 'created_at' => null,
+ 'updated_at' => null,
+ ];
+
+ /**
+ * @var ServiceFactory
+ */
+ private $serviceFactory;
+
+ /**
+ * @var ProcessorInterface
+ */
+ private $dataProcessor;
+
+ /**
+ * @var DataMerger
+ */
+ private $dataMerger;
+
+ /**
+ * @param ServiceFactory $serviceFactory
+ * @param ProcessorInterface $dataProcessor
+ */
+ public function __construct(
+ ServiceFactory $serviceFactory,
+ ProcessorInterface $dataProcessor,
+ DataMerger $dataMerger
+ ) {
+ $this->serviceFactory = $serviceFactory;
+ $this->dataProcessor = $dataProcessor;
+ $this->dataMerger = $dataMerger;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters. Same format as Product::DEFAULT_DATA. Custom attributes and extension attributes
+ * can be passed directly in the outer array instead of custom_attributes or extension_attributes.
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ $service = $this->serviceFactory->create(ProductRepositoryInterface::class, 'save');
+
+ return $service->execute(
+ [
+ 'product' => $this->prepareData($data)
+ ]
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function revert(DataObject $data): void
+ {
+ $service = $this->serviceFactory->create(ProductRepositoryInterface::class, 'deleteById');
+ $service->execute(
+ [
+ 'sku' => $data->getSku()
+ ]
+ );
+ }
+
+ /**
+ * Prepare product data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $data = $this->dataMerger->merge(self::DEFAULT_DATA, $data);
+ // remove category_links if empty in order for category_ids to be processed if exists
+ if (empty($data['extension_attributes']['category_links'])) {
+ unset($data['extension_attributes']['category_links']);
+ }
+
+ return $this->dataProcessor->process($this, $data);
+ }
+}
diff --git a/app/code/Magento/Catalog/Test/Fixture/Virtual.php b/app/code/Magento/Catalog/Test/Fixture/Virtual.php
new file mode 100644
index 0000000000000..35bf4de065d18
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Fixture/Virtual.php
@@ -0,0 +1,42 @@
+ Type::TYPE_VIRTUAL,
+ 'name' => 'Virtual Product%uniqid%',
+ 'sku' => 'virtual-product%uniqid%',
+ 'weight' => null,
+ ];
+
+ /**
+ * @inheritdoc
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ return parent::apply($this->prepareData($data));
+ }
+
+ /**
+ * Prepare product data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $data = array_merge(self::DEFAULT_DATA, $data);
+
+ return $data;
+ }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Fixture/Attribute.php b/app/code/Magento/ConfigurableProduct/Test/Fixture/Attribute.php
new file mode 100644
index 0000000000000..94766e0affd92
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Fixture/Attribute.php
@@ -0,0 +1,50 @@
+ 'select',
+ 'options' => [
+ [
+ 'label' => 'option1%uniqid%',
+ 'sort_order' => 0,
+ ],
+ [
+ 'label' => 'option2%uniqid%',
+ 'sort_order' => 1,
+ ]
+ ],
+ ];
+
+ /**
+ * @inheritdoc
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ $data = $this->prepareData($data);
+
+ return parent::apply($data);
+ }
+
+ /**
+ * Prepare attribute data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $data = array_merge(self::DEFAULT_DATA, $data);
+
+ return $data;
+ }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Fixture/Product.php b/app/code/Magento/ConfigurableProduct/Test/Fixture/Product.php
new file mode 100644
index 0000000000000..67e2d9c217fce
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Fixture/Product.php
@@ -0,0 +1,211 @@
+ null,
+ 'type_id' => Configurable::TYPE_CODE,
+ 'attribute_set_id' => 4,
+ 'name' => 'Configurable Product%uniqid%',
+ 'sku' => 'configurable-product%uniqid%',
+ 'price' => null,
+ 'weight' => null,
+ 'extension_attributes' => [
+ 'configurable_product_options' => [],
+ 'configurable_product_links' => [],
+ ]
+ ];
+
+ /**
+ * @var Config
+ */
+ private $eavConfig;
+
+ /**
+ * @var VariationMatrix
+ */
+ private $variationMatrix;
+
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
+ /**
+ * @param ServiceFactory $serviceFactory
+ * @param ProcessorInterface $dataProcessor
+ * @param DataMerger $dataMerger
+ * @param Config $eavConfig
+ * @param ProductRepositoryInterface $productRepository
+ * @param VariationMatrix $variationMatrix
+ */
+ public function __construct(
+ ServiceFactory $serviceFactory,
+ ProcessorInterface $dataProcessor,
+ DataMerger $dataMerger,
+ Config $eavConfig,
+ ProductRepositoryInterface $productRepository,
+ VariationMatrix $variationMatrix
+ ) {
+ parent::__construct($serviceFactory, $dataProcessor, $dataMerger);
+ $this->eavConfig = $eavConfig;
+ $this->variationMatrix = $variationMatrix;
+ $this->productRepository = $productRepository;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters. Same format as \Magento\Catalog\Test\Fixture\Product::DEFAULT_DATA.
+ * Custom attributes and extension attributes can be passed directly in the outer array instead of custom_attributes
+ * or extension_attributes.
+ * Additional fields:
+ * - $data['_options']: An array of attribute IDs, codes, or instances to use as configurable product options.
+ * - $data['_links']: An array of product IDs, SKUs or instances to associate to the configurable options.
+ * Products will be assigned to the variation in the same order as they are listed. Use 0 to skip a variation.
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ return parent::apply($this->prepareData($data));
+ }
+
+ /**
+ * Prepare product data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $data = array_merge(self::DEFAULT_DATA, $data);
+
+ if (isset($data['_options'])) {
+ $productIds = [];
+ $options = $this->prepareOptions($data);
+ if (isset($data['_links'])) {
+ $links = $this->prepareLinks($data);
+ $this->associateProducts($links, $options);
+ // remove holes
+ $productIds = array_values(array_filter($links));
+ }
+ unset($data['_options'], $data['_links']);
+ $data['extension_attributes']['configurable_product_options'] = $options;
+ $data['extension_attributes']['configurable_product_links'] = $productIds;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Generate configurable options
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareOptions(array $data): array
+ {
+ $options = [];
+ foreach ($data['_options'] as $index => $attribute) {
+ $attributeId = $attribute instanceof AttributeInterface ? $attribute->getAttributeId() : $attribute;
+ $attributeObject = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeId);
+ $values = [];
+ foreach ($attributeObject->getOptions() as $option) {
+ if ($option->getValue()) {
+ $values[] = [
+ 'value_index' => $option->getValue(),
+ ];
+ }
+ }
+ $options[] = [
+ 'attribute_id' => $attributeObject->getId(),
+ 'label' => $attributeObject->getStoreLabel(),
+ 'position' => $index,
+ 'values' => $values,
+ ];
+ }
+ return $options;
+ }
+
+ /**
+ * Prepare configurable associated products
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareLinks(array $data): array
+ {
+ $links = [];
+ foreach ($data['_links'] as $link) {
+ if (!is_numeric($link)) {
+ $sku = $link instanceof ProductInterface
+ ? $link->getSku()
+ : $link;
+ $product = $this->productRepository->get($sku);
+ $productId = $product->getId();
+ } else {
+ $productId = $link;
+ }
+ $links[] = (int) $productId;
+ }
+
+ return $links;
+ }
+
+ /**
+ * Associate provided products list to configurable options
+ *
+ * @param array $links List of product IDs to associate to each variation in order.
+ * 0 in the list means no product will be associated to the corresponding variation.
+ * @param array $options
+ * @return void
+ */
+ private function associateProducts(array $links, array $options): void
+ {
+ $variations = $this->variationMatrix->getVariations(
+ array_map(
+ static function (array $option) {
+ return [
+ 'attribute_id' => $option['attribute_id'],
+ 'values' => $option['values'],
+ 'options' => array_map(
+ static function (array $value) {
+ return ['value' => $value['value_index']];
+ },
+ $option['values']
+ )
+ ];
+ },
+ $options
+ )
+ );
+ $variationIndex = 0;
+ foreach ($variations as $variation) {
+ foreach ($variation as $attributeId => $valueInfo) {
+ if (isset($links[$variationIndex]) && $links[$variationIndex] !== 0) {
+ $attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeId);
+ $product = $this->productRepository->getById($links[$variationIndex]);
+ $product->setCustomAttribute($attribute->getAttributeCode(), $valueInfo['value']);
+ $this->productRepository->save($product);
+ }
+ $variationIndex++;
+ }
+ }
+ }
+}
diff --git a/app/code/Magento/GroupedProduct/Test/Fixture/Product.php b/app/code/Magento/GroupedProduct/Test/Fixture/Product.php
new file mode 100644
index 0000000000000..dc6640dbb7f79
--- /dev/null
+++ b/app/code/Magento/GroupedProduct/Test/Fixture/Product.php
@@ -0,0 +1,122 @@
+ Grouped::TYPE_CODE,
+ 'name' => 'Grouped Product%uniqid%',
+ 'sku' => 'grouped-product%uniqid%',
+ 'price' => null,
+ 'weight' => null,
+ 'product_links' => [],
+ ];
+
+ private const DEFAULT_PRODUCT_LINK_DATA = [
+ 'sku' => null,
+ 'position' => 1,
+ 'qty' => 1,
+ ];
+
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
+ /**
+ * @param ServiceFactory $serviceFactory
+ * @param ProcessorInterface $dataProcessor
+ * @param DataMerger $dataMerger
+ * @param ProductRepositoryInterface $productRepository
+ */
+ public function __construct(
+ ServiceFactory $serviceFactory,
+ ProcessorInterface $dataProcessor,
+ DataMerger $dataMerger,
+ ProductRepositoryInterface $productRepository
+ ) {
+ parent::__construct($serviceFactory, $dataProcessor, $dataMerger);
+ $this->productRepository = $productRepository;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ return parent::apply($this->prepareData($data));
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters. Same format as \Magento\Catalog\Test\Fixture\Product::DEFAULT_DATA.
+ * Custom attributes and extension attributes can be passed directly in the outer array instead of custom_attributes
+ * or extension_attributes.
+ * - $data['product_links']: An array of product IDs, SKUs or instances to associate to the grouped product. For
+ * advanced configuration, use an array{sku: string, position: int, qty: int}
+ */
+ private function prepareData(array $data): array
+ {
+ $data = array_merge(self::DEFAULT_DATA, $data);
+ $data['product_links'] = $this->prepareLinksData($data);
+
+ return $data;
+ }
+
+ /**
+ * Prepare links data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareLinksData(array $data): array
+ {
+ $links = [];
+
+ $position = 1;
+ foreach ($data['product_links'] as $link) {
+ $linkData = [];
+ $defaultLinkData = self::DEFAULT_PRODUCT_LINK_DATA;
+ $defaultLinkData['position'] = $position;
+ if (is_numeric($link)) {
+ $product = $this->productRepository->getById($link);
+ } elseif (is_string($link)) {
+ $product = $this->productRepository->get($link);
+ } elseif ($link instanceof ProductInterface) {
+ $product = $this->productRepository->get($link->getSku());
+ } else {
+ $linkData = $link instanceof DataObject ? $link->toArray() : $link;
+ $product = $this->productRepository->get($linkData['sku']);
+ }
+
+ $linkData += $defaultLinkData;
+ $links[] = [
+ 'sku' => $data['sku'],
+ 'link_type' => 'associated',
+ 'linked_product_sku' => $product->getSku(),
+ 'linked_product_type' => $product->getTypeId(),
+ 'position' => $linkData['position'],
+ 'extension_attributes' => [
+ 'qty' => $linkData['qty']
+ ],
+ ];
+ $position++;
+ }
+
+ return $links;
+ }
+}
diff --git a/app/code/Magento/Quote/Test/Fixture/AddProductToCart.php b/app/code/Magento/Quote/Test/Fixture/AddProductToCart.php
new file mode 100644
index 0000000000000..27d764a224a6a
--- /dev/null
+++ b/app/code/Magento/Quote/Test/Fixture/AddProductToCart.php
@@ -0,0 +1,58 @@
+cartRepository = $cartRepository;
+ $this->productRepository = $productRepository;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters
+ *
+ * $data = [
+ * 'cart_id' => (int) Cart ID. Required.
+ * 'product_id' => (int) Product ID. Required.
+ * 'qty' => (int) Quantity. Optional. Default: 1.
+ * ]
+ *
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ $cart = $this->cartRepository->get($data['cart_id']);
+ $product = $this->productRepository->getById($data['product_id']);
+ $catItem = $cart->addProduct($product, $data['qty'] ?? 1);
+ $this->cartRepository->save($cart);
+ return $catItem;
+ }
+}
diff --git a/app/code/Magento/Quote/Test/Fixture/GuestCart.php b/app/code/Magento/Quote/Test/Fixture/GuestCart.php
new file mode 100644
index 0000000000000..ae724f36f50e3
--- /dev/null
+++ b/app/code/Magento/Quote/Test/Fixture/GuestCart.php
@@ -0,0 +1,91 @@
+cartRepository = $cartRepository;
+ $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
+ $this->guestCartManagement = $guestCartManagement;
+ $this->quoteResource = $quoteResource;
+ $this->quoteFactory = $quoteFactory;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ $maskId = $this->guestCartManagement->createEmptyCart();
+ $cartId = $this->maskedQuoteIdToQuoteId->execute($maskId);
+
+ return $this->cartRepository->get($cartId);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function revert(DataObject $data): void
+ {
+ /** @var Quote $cart */
+ $cart = $data;
+ $quote = $this->quoteFactory->create();
+ $this->quoteResource->load($quote, $cart->getId());
+ if ($quote->getId()) {
+ $this->cartRepository->delete($cart);
+ }
+ }
+}
diff --git a/app/code/Magento/Quote/Test/Fixture/SetBillingAddress.php b/app/code/Magento/Quote/Test/Fixture/SetBillingAddress.php
new file mode 100644
index 0000000000000..5ad4aaef9e66e
--- /dev/null
+++ b/app/code/Magento/Quote/Test/Fixture/SetBillingAddress.php
@@ -0,0 +1,66 @@
+ 3340000000,
+ AddressInterface::KEY_POSTCODE => 36104,
+ AddressInterface::KEY_COUNTRY_ID => 'US',
+ AddressInterface::KEY_CITY => 'Montgomery',
+ AddressInterface::KEY_COMPANY => 'Magento',
+ AddressInterface::KEY_STREET => ['Green str, 67'],
+ AddressInterface::KEY_FIRSTNAME => 'John',
+ AddressInterface::KEY_LASTNAME => 'Doe',
+ AddressInterface::KEY_REGION_ID => 1,
+ ];
+
+ /**
+ * @var ServiceFactory
+ */
+ private $serviceFactory;
+
+ /**
+ * @param ServiceFactory $serviceFactory
+ */
+ public function __construct(
+ ServiceFactory $serviceFactory
+ ) {
+ $this->serviceFactory = $serviceFactory;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters
+ *
+ * $data = [
+ * 'cart_id' => (int) Cart ID. Required.
+ * 'address' => (array) Address Data. Optional. Default: SetBillingAddress::DEFAULT_DATA
+ * ]
+ *
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ $service = $this->serviceFactory->create(BillingAddressManagementInterface::class, 'assign');
+
+ $service->execute(
+ [
+ 'cartId' => $data['cart_id'],
+ 'address' => array_merge(self::DEFAULT_DATA, $data['address'] ?? [])
+ ]
+ );
+ return null;
+ }
+}
diff --git a/app/code/Magento/Quote/Test/Fixture/SetShippingAddress.php b/app/code/Magento/Quote/Test/Fixture/SetShippingAddress.php
new file mode 100644
index 0000000000000..e12b813d99b71
--- /dev/null
+++ b/app/code/Magento/Quote/Test/Fixture/SetShippingAddress.php
@@ -0,0 +1,63 @@
+ 3340000000,
+ AddressInterface::KEY_POSTCODE => 36104,
+ AddressInterface::KEY_COUNTRY_ID => 'US',
+ AddressInterface::KEY_CITY => 'Montgomery',
+ AddressInterface::KEY_COMPANY => 'Magento',
+ AddressInterface::KEY_STREET => ['Green str, 67'],
+ AddressInterface::KEY_LASTNAME => 'Doe',
+ AddressInterface::KEY_FIRSTNAME => 'John',
+ AddressInterface::KEY_REGION_ID => 1,
+ ];
+
+ /**
+ * @var ServiceFactory
+ */
+ private $serviceFactory;
+
+ public function __construct(
+ ServiceFactory $serviceFactory
+ ) {
+ $this->serviceFactory = $serviceFactory;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters
+ *
+ * $data = [
+ * 'cart_id' => (int) Cart ID. Required.
+ * 'address' => (array) Address Data. Optional. Default: SetShippingAddress::DEFAULT_DATA
+ * ]
+ *
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ $service = $this->serviceFactory->create(ShippingAddressManagementInterface::class, 'assign');
+
+ $service->execute(
+ [
+ 'cartId' => $data['cart_id'],
+ 'address' => array_merge(self::DEFAULT_DATA, $data['address'] ?? [])
+ ]
+ );
+ return null;
+ }
+}
diff --git a/app/code/Magento/Store/Test/Fixture/Group.php b/app/code/Magento/Store/Test/Fixture/Group.php
new file mode 100644
index 0000000000000..da768b72bb62e
--- /dev/null
+++ b/app/code/Magento/Store/Test/Fixture/Group.php
@@ -0,0 +1,112 @@
+ 'test_store_group%uniqid%',
+ 'name' => 'Test Store Group%uniqid%',
+ ];
+
+ /**
+ * @var GroupInterfaceFactory
+ */
+ private $groupFactory;
+
+ /**
+ * @var GroupResource
+ */
+ private $groupResource;
+
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
+
+ /**
+ * @var DefaultCategory
+ */
+ private $defaultCategory;
+
+ /**
+ * @var ProcessorInterface
+ */
+ private $dataProcessor;
+
+ /**
+ * @param GroupInterfaceFactory $groupFactory
+ * @param GroupResource $groupResource
+ * @param StoreManagerInterface $storeManager
+ * @param DefaultCategory $defaultCategory
+ * @param ProcessorInterface $dataProcessor
+ */
+ public function __construct(
+ GroupInterfaceFactory $groupFactory,
+ GroupResource $groupResource,
+ StoreManagerInterface $storeManager,
+ DefaultCategory $defaultCategory,
+ ProcessorInterface $dataProcessor
+ ) {
+ $this->groupFactory = $groupFactory;
+ $this->groupResource = $groupResource;
+ $this->storeManager = $storeManager;
+ $this->defaultCategory = $defaultCategory;
+ $this->dataProcessor = $dataProcessor;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters
+ *
+ * $data = [
+ * 'id' => (int) ID. Optional.
+ * 'code' => (string) Code. Optional.
+ * 'name' => (string) Name. Optional.
+ * 'website_id' => (int) Website ID. Optional. Default: default website.
+ * 'root_category_id' => (int) Root Category ID. Optional. Default: default root category.
+ * 'default_store_id' => (int) Default Store ID. Optional.
+ * ]
+ *
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ /** @var GroupInterface $group */
+ $group = $this->groupFactory->create();
+ $group->setData($this->prepareData($data));
+ $this->groupResource->save($group);
+ $this->storeManager->reinitStores();
+
+ return $group;
+ }
+
+ /**
+ * Prepare store group data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $defaultData = self::DEFAULT_DATA;
+ $defaultData['root_category_id'] = $this->defaultCategory->getId();
+ $defaultData['website_id'] = $this->storeManager->getDefaultStoreView()->getWebsiteId();
+ $data = array_merge($defaultData, $data);
+
+ return $this->dataProcessor->process($this, $data);
+ }
+}
diff --git a/app/code/Magento/Store/Test/Fixture/Store.php b/app/code/Magento/Store/Test/Fixture/Store.php
new file mode 100644
index 0000000000000..744f079c37f7a
--- /dev/null
+++ b/app/code/Magento/Store/Test/Fixture/Store.php
@@ -0,0 +1,128 @@
+ 'test_store_view%uniqid%',
+ 'name' => 'Test Store View%uniqid%',
+ 'sort_order' => '0',
+ 'is_active' => '1'
+ ];
+
+ /**
+ * @var StoreInterfaceFactory
+ */
+ private $storeFactory;
+
+ /**
+ * @var StoreResource
+ */
+ private $storeResource;
+
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
+
+ /**
+ * @var ProcessorInterface
+ */
+ private $dataProcessor;
+
+ /**
+ * @param StoreInterfaceFactory $storeFactory
+ * @param StoreResource $storeResource
+ * @param StoreManagerInterface $storeManager
+ * @param ProcessorInterface $dataProcessor
+ */
+ public function __construct(
+ StoreInterfaceFactory $storeFactory,
+ StoreResource $storeResource,
+ StoreManagerInterface $storeManager,
+ ProcessorInterface $dataProcessor
+ ) {
+ $this->storeFactory = $storeFactory;
+ $this->storeResource = $storeResource;
+ $this->storeManager = $storeManager;
+ $this->dataProcessor = $dataProcessor;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters
+ *
+ * $data = [
+ * 'id' => (int) ID. Optional.
+ * 'code' => (string) Code. Optional.
+ * 'name' => (string) Name. Optional.
+ * 'website_id' => (int) Website ID. Optional. Default: default website.
+ * 'store_group_id' => (int) Store Group ID. Optional. Default: default store group.
+ * 'is_active' => (int) Is Active. Optional. Default: 1
+ * 'sort_order' => (int) Sort Order. Optional. Default: 0
+ * ]
+ *
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ /** @var StoreInterface $store */
+ $store = $this->storeFactory->create();
+ $store->setData($this->prepareData($data));
+ $this->storeResource->save($store);
+ $this->storeManager->reinitStores();
+
+ return $store;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function revert(DataObject $data): void
+ {
+ /** @var StoreInterface $store */
+ $store = $this->storeFactory->create();
+ $this->storeResource->load($store, $data->getCode(), 'code');
+ if ($store->getId()) {
+ $this->storeResource->delete($store);
+ }
+ $this->storeManager->reinitStores();
+ }
+
+ /**
+ * Prepare store data
+ *
+ * @param array $data
+ * @return array
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ private function prepareData(array $data): array
+ {
+ $data = array_merge(self::DEFAULT_DATA, $data);
+
+ if (!isset($data['store_group_id']) && !isset($data['website_id'])) {
+ $data['store_group_id'] = $this->storeManager->getDefaultStoreView()->getStoreGroupId();
+ }
+ if (isset($data['store_group_id']) && !isset($data['website_id'])) {
+ $data['website_id'] = $this->storeManager->getGroup($data['store_group_id'])->getWebsiteId();
+ } elseif (!isset($data['store_group_id']) && isset($data['website_id'])) {
+ $data['store_group_id'] = $this->storeManager->getWebsite($data['website_id'])->getDefaultGroupId();
+ }
+ $data['group_id'] = $data['store_group_id'];
+
+ return $this->dataProcessor->process($this, $data);
+ }
+}
diff --git a/app/code/Magento/Store/Test/Fixture/Website.php b/app/code/Magento/Store/Test/Fixture/Website.php
new file mode 100644
index 0000000000000..b491f0e593b41
--- /dev/null
+++ b/app/code/Magento/Store/Test/Fixture/Website.php
@@ -0,0 +1,115 @@
+ 'test_website%uniqid%',
+ 'name' => 'Test Website%uniqid%',
+ 'is_default' => '0'
+ ];
+
+ /**
+ * @var WebsiteInterfaceFactory
+ */
+ private $websiteFactory;
+
+ /**
+ * @var WebsiteResource
+ */
+ private $websiteResource;
+
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
+
+ /**
+ * @var ProcessorInterface
+ */
+ private $dataProcessor;
+
+ /**
+ * @param WebsiteInterfaceFactory $websiteFactory
+ * @param WebsiteResource $websiteResource
+ * @param StoreManagerInterface $storeManager
+ * @param ProcessorInterface $dataProcessor
+ */
+ public function __construct(
+ WebsiteInterfaceFactory $websiteFactory,
+ WebsiteResource $websiteResource,
+ StoreManagerInterface $storeManager,
+ ProcessorInterface $dataProcessor
+ ) {
+ $this->websiteFactory = $websiteFactory;
+ $this->websiteResource = $websiteResource;
+ $this->storeManager = $storeManager;
+ $this->dataProcessor = $dataProcessor;
+ }
+
+ /**
+ * {@inheritdoc}
+ * @param array $data Parameters
+ *
+ * $data = [
+ * 'id' => (int) ID. Optional.
+ * 'code' => (string) Code. Optional.
+ * 'name' => (string) Name. Optional.
+ * 'default_group_id' => (int) Default Group ID. Optional.
+ * 'is_default' => (int) Is Default. Optional. Default: 0.
+ * 'sort_order' => (int) Sort Order. Optional. Default: 0.
+ * ]
+ *
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ /** @var WebsiteInterface $website */
+ $website = $this->websiteFactory->create();
+ $website->setData($this->prepareData($data));
+ $this->websiteResource->save($website);
+ $this->storeManager->reinitStores();
+
+ return $website;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function revert(DataObject $data): void
+ {
+ /** @var WebsiteInterface $website */
+ $website = $this->websiteFactory->create();
+ $this->websiteResource->load($website, $data->getCode(), 'code');
+ if ($website->getId()) {
+ $this->websiteResource->delete($website);
+ }
+ $this->storeManager->reinitStores();
+ }
+
+ /**
+ * Prepare website data
+ *
+ * @param array $data
+ * @return array
+ */
+ private function prepareData(array $data): array
+ {
+ $data = array_merge(self::DEFAULT_DATA, $data);
+
+ return $this->dataProcessor->process($this, $data);
+ }
+}
diff --git a/dev/tests/api-functional/framework/bootstrap.php b/dev/tests/api-functional/framework/bootstrap.php
index 516d1e01a5322..230740b076537 100644
--- a/dev/tests/api-functional/framework/bootstrap.php
+++ b/dev/tests/api-functional/framework/bootstrap.php
@@ -107,6 +107,9 @@
Magento\TestFramework\Workaround\Override\Fixture\Resolver::setInstance(
new \Magento\TestFramework\WebapiWorkaround\Override\Fixture\Resolver($overrideConfig)
);
+ Magento\TestFramework\Fixture\DataFixtureStorageManager::setStorage(
+ new Magento\TestFramework\Fixture\DataFixtureStorage()
+ );
\Magento\TestFramework\Workaround\Override\Config::setInstance($overrideConfig);
unset($bootstrap, $application, $settings, $shell, $overrideConfig);
} catch (\Exception $e) {
diff --git a/dev/tests/api-functional/phpunit_graphql.xml.dist b/dev/tests/api-functional/phpunit_graphql.xml.dist
index fc49642eaa721..650d6f33cfef6 100644
--- a/dev/tests/api-functional/phpunit_graphql.xml.dist
+++ b/dev/tests/api-functional/phpunit_graphql.xml.dist
@@ -110,6 +110,9 @@
Override
+
+ magentoDataFixtureDataProvider
+
diff --git a/dev/tests/api-functional/phpunit_rest.xml.dist b/dev/tests/api-functional/phpunit_rest.xml.dist
index f12905369245b..691320476029e 100644
--- a/dev/tests/api-functional/phpunit_rest.xml.dist
+++ b/dev/tests/api-functional/phpunit_rest.xml.dist
@@ -116,6 +116,9 @@
Override
+
+ magentoDataFixtureDataProvider
+
diff --git a/dev/tests/api-functional/phpunit_soap.xml.dist b/dev/tests/api-functional/phpunit_soap.xml.dist
index 6897807a3e052..590f861ee0f49 100644
--- a/dev/tests/api-functional/phpunit_soap.xml.dist
+++ b/dev/tests/api-functional/phpunit_soap.xml.dist
@@ -115,6 +115,9 @@
Override
+
+ magentoDataFixtureDataProvider
+
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php
index 2e8eedf96b0f8..7dedeb3f9bc96 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php
@@ -17,6 +17,8 @@
use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator;
use Magento\Integration\Api\AdminTokenServiceInterface;
use Magento\Store\Model\Store;
+use Magento\TestFramework\Fixture\DataFixtureStorage;
+use Magento\TestFramework\Fixture\DataFixtureStorageManager;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\WebapiAbstract;
use Magento\UrlRewrite\Model\Storage\DbStorage;
@@ -29,9 +31,12 @@
*/
class CategoryRepositoryTest extends WebapiAbstract
{
- const RESOURCE_PATH = '/V1/categories';
- const SERVICE_NAME = 'catalogCategoryRepositoryV1';
+ private const RESOURCE_PATH = '/V1/categories';
+ private const SERVICE_NAME = 'catalogCategoryRepositoryV1';
+ /**
+ * @var int
+ */
private $modelId = 333;
/**
@@ -54,6 +59,11 @@ class CategoryRepositoryTest extends WebapiAbstract
*/
private $createdCategories;
+ /**
+ * @var DataFixtureStorage
+ */
+ private $fixtures;
+
/**
* @inheritDoc
*/
@@ -64,6 +74,7 @@ protected function setUp(): void
$this->roleFactory = Bootstrap::getObjectManager()->get(RoleFactory::class);
$this->rulesFactory = Bootstrap::getObjectManager()->get(RulesFactory::class);
$this->adminTokens = Bootstrap::getObjectManager()->get(AdminTokenServiceInterface::class);
+ $this->fixtures = Bootstrap::getObjectManager()->get(DataFixtureStorageManager::class)->getStorage();
}
/**
@@ -230,11 +241,11 @@ private function buildExceptionMessage(int $categoryId): string
}
/**
- * @magentoApiDataFixture Magento/Catalog/_files/category.php
+ * @magentoApiDataFixture Magento\Catalog\Test\Fixture\Category as:category
*/
public function testUpdate()
{
- $categoryId = 333;
+ $categoryId = $this->fixtures->get('category')->getId();
$categoryData = [
'name' => 'Update Category Test',
'is_active' => false,
@@ -285,13 +296,13 @@ public function testUpdateWithDefaultSortByAttribute()
}
/**
- * @magentoApiDataFixture Magento/Catalog/_files/category.php
+ * @magentoApiDataFixture Magento\Catalog\Test\Fixture\Category as:category
*/
public function testUpdateUrlKey()
{
$this->_markTestAsRestOnly('Functionality available in REST mode only.');
- $categoryId = 333;
+ $categoryId = $this->fixtures->get('category')->getId();
$categoryData = [
'name' => 'Update Category Test Old Name',
'custom_attributes' => [
@@ -590,11 +601,11 @@ public function testSaveDesign(): void
/**
* Check if repository does not override default values for attributes out of request
*
- * @magentoApiDataFixture Magento/Catalog/_files/category.php
+ * @magentoApiDataFixture Magento\Catalog\Test\Fixture\Category as:category
*/
public function testUpdateScopeAttribute()
{
- $categoryId = 333;
+ $categoryId = $this->fixtures->get('category')->getId();
$categoryData = [
'name' => 'Scope Specific Value',
];
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php
index d080f87d6a148..81f27dfafd332 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php
@@ -17,6 +17,7 @@
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
+use Magento\TestFramework\Fixture\DataFixtureStorageManager;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\ObjectManager;
use Magento\TestFramework\TestCase\GraphQl\ResponseContainsErrorsException;
@@ -49,12 +50,18 @@ class CategoryTest extends GraphQlAbstract
*/
private $metadataPool;
+ /**
+ * @var \Magento\TestFramework\Fixture\DataFixtureStorage
+ */
+ private $fixtures;
+
protected function setUp(): void
{
$this->objectManager = Bootstrap::getObjectManager();
$this->categoryRepository = $this->objectManager->get(CategoryRepository::class);
$this->store = $this->objectManager->get(Store::class);
$this->metadataPool = $this->objectManager->get(MetadataPool::class);
+ $this->fixtures = Bootstrap::getObjectManager()->get(DataFixtureStorageManager::class)->getStorage();
}
/**
@@ -275,11 +282,11 @@ public function testCategoriesTreeWithDisabledCategory()
}
/**
- * @magentoApiDataFixture Magento/Catalog/_files/categories.php
+ * @magentoApiDataFixture Magento\Catalog\Test\Fixture\Category with:{"name":"Category 1.2"} as:category
*/
public function testGetCategoryById()
{
- $categoryId = 13;
+ $categoryId = $this->fixtures->get('category')->getId();
$query = <<graphQlQuery($query);
self::assertEquals('Category 1.2', $response['category']['name']);
- self::assertEquals(13, $response['category']['id']);
+ self::assertEquals($categoryId, $response['category']['id']);
}
/**
- * @magentoApiDataFixture Magento/Catalog/_files/categories.php
+ * @magentoApiDataFixture Magento\Catalog\Test\Fixture\Category with:{"is_active":false} as:category
*/
public function testGetDisabledCategory()
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Category doesn\'t exist');
- $categoryId = 8;
+ $categoryId = $this->fixtures->get('category')->getId();
$query = <<getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ $this->quoteIdToMaskedQuoteIdInterface = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class);
+ $this->fixtures = $objectManager->get(DataFixtureStorageManager::class)->getStorage();
}
/**
@@ -179,15 +194,17 @@ public function testGetCartTotalsWithEmptyCart()
}
/**
- * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
- * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
- * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
- * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
- * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ * @magentoApiDataFixture Magento\Catalog\Test\Fixture\Product as:product
+ * @magentoApiDataFixture Magento\Quote\Test\Fixture\GuestCart as:cart
+ * @magentoApiDataFixture Magento\Quote\Test\Fixture\AddProductToCart as:item1
+ * @magentoApiDataFixture Magento\Quote\Test\Fixture\SetBillingAddress with:{"cart_id":"$cart.id$"}
+ * @magentoApiDataFixture Magento\Quote\Test\Fixture\SetShippingAddress with:{"cart_id":"$cart.id$"}
+ * @magentoDataFixtureDataProvider {"item1":{"cart_id":"$cart.id$","product_id":"$product.id$","qty":2}}
*/
public function testGetTotalsWithNoTaxApplied()
{
- $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $cart = $this->fixtures->get('cart');
+ $maskedQuoteId = $this->quoteIdToMaskedQuoteIdInterface->execute((int) $cart->getId());
$query = $this->getQuery($maskedQuoteId);
$response = $this->graphQlQuery($query);
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php
index 9afbb2db728e1..fec8760fa8449 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php
@@ -7,13 +7,14 @@
namespace Magento\TestFramework\Annotation;
-use Magento\TestFramework\Annotation\TestCaseAnnotation;
+use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
-use PHPUnit\Framework\Exception;
use PHPUnit\Framework\TestCase;
/**
* Class consist of dataFixtures base logic
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
abstract class AbstractDataFixture
{
@@ -46,7 +47,19 @@ protected function _getFixtures(TestCase $test, $scope = null)
$resolver->setCurrentFixtureType($annotationKey);
$annotations = TestCaseAnnotation::getInstance()->getAnnotations($test);
$annotations = $scope === null ? $this->getAnnotations($test) : $annotations[$scope];
- $existingFixtures = $annotations[$annotationKey] ?? [];
+ $existingFixtures = [];
+ $objectManager = Bootstrap::getObjectManager();
+ $fixtureDirectivesParser = $objectManager->get(DataFixtureDirectivesParser::class);
+ $fixtureDataProviderAnnotation = $objectManager->get(DataFixtureDataProvider::class);
+ $fixtureDataProvider = $fixtureDataProviderAnnotation->getDataProvider($test);
+ foreach ($annotations[$annotationKey] ?? [] as $fixture) {
+ $metadata = $fixtureDirectivesParser->parse($fixture);
+ if ($metadata['name'] && empty($metadata['data']) && isset($fixtureDataProvider[$metadata['name']])) {
+ $metadata['data'] = $fixtureDataProvider[$metadata['name']];
+ }
+ $existingFixtures[] = $metadata;
+ }
+
/* Need to be applied even test does not have added fixtures because fixture can be added via config */
$this->fixtures[$annotationKey][$this->getTestKey($test)] = $resolver->applyDataFixtures(
$test,
@@ -72,35 +85,6 @@ protected function getAnnotations(TestCase $test): array
return array_replace((array)$annotations['class'], (array)$annotations['method']);
}
- /**
- * Execute single fixture script
- *
- * @param string|array $fixture
- * @return void
- * @throws \Exception
- */
- protected function _applyOneFixture($fixture)
- {
- try {
- if (is_callable($fixture)) {
- call_user_func($fixture);
- } else {
- require $fixture;
- }
- } catch (\Exception $e) {
- throw new Exception(
- sprintf(
- "Error in fixture: %s.\n %s\n %s",
- json_encode($fixture),
- $e->getMessage(),
- $e->getTraceAsString()
- ),
- 500,
- $e
- );
- }
- }
-
/**
* Execute fixture scripts if any
*
@@ -110,17 +94,18 @@ protected function _applyOneFixture($fixture)
*/
protected function _applyFixtures(array $fixtures, TestCase $test)
{
- /** @var \Magento\TestFramework\Annotation\TestsIsolation $testsIsolation */
- $testsIsolation = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- \Magento\TestFramework\Annotation\TestsIsolation::class
- );
+ $objectManager = Bootstrap::getObjectManager();
+ $testsIsolation = $objectManager->get(TestsIsolation::class);
$dbIsolationState = $this->getDbIsolationState($test);
$testsIsolation->createDbSnapshot($test, $dbIsolationState);
-
+ $dataFixtureSetup = $objectManager->get(DataFixtureSetup::class);
/* Execute fixture scripts */
- foreach ($fixtures as $oneFixture) {
- $this->_applyOneFixture($oneFixture);
- $this->_appliedFixtures[] = $oneFixture;
+ foreach ($fixtures as $fixture) {
+ if (is_callable([get_class($test), $fixture['factory']])) {
+ $fixture['factory'] = get_class($test) . '::' . $fixture['factory'];
+ }
+ $fixture['result'] = $dataFixtureSetup->apply($fixture);
+ $this->_appliedFixtures[] = $fixture;
}
$resolver = Resolver::getInstance();
$resolver->setCurrentFixtureType(null);
@@ -134,35 +119,20 @@ protected function _applyFixtures(array $fixtures, TestCase $test)
*/
protected function _revertFixtures(?TestCase $test = null)
{
+ $objectManager = Bootstrap::getObjectManager();
+ $dataFixtureSetup = $objectManager->get(DataFixtureSetup::class);
$resolver = Resolver::getInstance();
$resolver->setCurrentFixtureType($this->getAnnotation());
$appliedFixtures = array_reverse($this->_appliedFixtures);
foreach ($appliedFixtures as $fixture) {
- if (is_callable($fixture)) {
- $fixture[1] .= 'Rollback';
- if (is_callable($fixture)) {
- $this->_applyOneFixture($fixture);
- }
- } else {
- $fileInfo = pathinfo($fixture);
- $extension = '';
- if (isset($fileInfo['extension'])) {
- $extension = '.' . $fileInfo['extension'];
- }
- $rollbackScript = $fileInfo['dirname'] . '/' . $fileInfo['filename'] . '_rollback' . $extension;
- if (file_exists($rollbackScript)) {
- $this->_applyOneFixture($rollbackScript);
- }
- }
+ $dataFixtureSetup->revert($fixture);
}
$this->_appliedFixtures = [];
$resolver->setCurrentFixtureType(null);
if (null !== $test) {
- /** @var \Magento\TestFramework\Annotation\TestsIsolation $testsIsolation */
- $testsIsolation = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- \Magento\TestFramework\Annotation\TestsIsolation::class
- );
+ /** @var TestsIsolation $testsIsolation */
+ $testsIsolation = $objectManager->get(TestsIsolation::class);
$dbIsolationState = $this->getDbIsolationState($test);
$testsIsolation->checkTestIsolation($test, $dbIsolationState);
}
@@ -177,9 +147,7 @@ protected function _revertFixtures(?TestCase $test = null)
protected function getDbIsolationState(TestCase $test)
{
$annotations = $this->getAnnotations($test);
- return isset($annotations[DbIsolation::MAGENTO_DB_ISOLATION])
- ? $annotations[DbIsolation::MAGENTO_DB_ISOLATION]
- : null;
+ return $annotations[DbIsolation::MAGENTO_DB_ISOLATION] ?? null;
}
/**
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureDataProvider.php
new file mode 100644
index 0000000000000..28f89afb599ad
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureDataProvider.php
@@ -0,0 +1,72 @@
+serializer = $serializer;
+ }
+
+ /**
+ * Return data from fixture data provider
+ *
+ * @param TestCase $test
+ * @return array
+ */
+ public function getDataProvider(TestCase $test): array
+ {
+ $annotations = TestCaseAnnotation::getInstance()->getAnnotations($test);
+ $dataProviders = array_merge(
+ $annotations['class'][self::ANNOTATION] ?? [],
+ $annotations['method'][self::ANNOTATION] ?? []
+ );
+ $result = [];
+ foreach (array_reverse($dataProviders) as $dataProvider) {
+ if (isset($dataProvider)) {
+ if (is_callable([$test, $dataProvider])) {
+ $data = $test->$dataProvider();
+ } elseif (is_callable($dataProvider)) {
+ $data = $dataProvider();
+ } else {
+ try {
+ $data = $this->serializer->unserialize($dataProvider);
+ } catch (\InvalidArgumentException $exception) {
+ throw new Exception('Fixture data provider must be a callable or valid JSON');
+ }
+ }
+ $result += $data;
+ }
+
+ }
+
+ return $result;
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureDirectivesParser.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureDirectivesParser.php
new file mode 100644
index 0000000000000..6932d80ce2540
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureDirectivesParser.php
@@ -0,0 +1,78 @@
+serializer = $serializer;
+ }
+
+ /**
+ * Parse data fixture directives
+ *
+ * @param string $fixture
+ * @return array
+ * @throws LocalizedException
+ */
+ public function parse(string $fixture): array
+ {
+ list($factory, $directives) = array_pad(array_values(array_filter(explode(' ', $fixture, 2))), 2, '');
+ $name = null;
+ $data = [];
+ if ($directives) {
+ $json = '{}';
+ $with = strpos($directives, 'with:');
+ if ($with !== false) {
+ $jsonStart = $with + 5;
+ $jsonEnd = strrpos($directives, '}');
+ $json = trim(substr($directives, $jsonStart, $jsonEnd - $jsonStart + 1));
+ $directives = substr_replace($directives, '', $jsonStart, $jsonEnd - $jsonStart + 1);
+ }
+ foreach (array_filter(explode(' ', $directives)) as $pair) {
+ list($directive, $value) = explode(':', $pair, 2);
+ switch ($directive) {
+ case 'with':
+ $data = $this->serializer->unserialize($json);
+ break;
+ case 'as':
+ $name = $value;
+ break;
+ default:
+ throw new \InvalidArgumentException("Unknown data fixture directive '$directive'");
+ }
+ }
+ }
+ if (strpos($factory, '\\') !== false && !class_exists($factory) && !is_callable($factory)) {
+ // usage of a single directory separator symbol streamlines search across the source code
+ throw new LocalizedException(__('Directory separator "\\" is prohibited in fixture declaration.'));
+ }
+
+ return [
+ 'name' => $name,
+ 'factory' => $factory,
+ 'data' => $data,
+ ];
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureSetup.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureSetup.php
new file mode 100644
index 0000000000000..1edb1b058091d
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureSetup.php
@@ -0,0 +1,147 @@
+registry = $registry;
+ $this->dataFixtureFactory = $dataFixtureFactory;
+ }
+
+ /**
+ * Applies data fixture and returns the result.
+ *
+ * @param array $fixture
+ * @return DataObject|null
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function apply(array $fixture): ?DataObject
+ {
+ $data = $this->resolveVariables($fixture['data'] ?? []);
+ try {
+ $factory = $this->dataFixtureFactory->create($fixture['factory']);
+ $result = $factory->apply($data);
+ } catch (\Throwable $exception) {
+ throw new Exception(
+ sprintf(
+ "Unable to apply fixture%s: %s.\n%s\n%s",
+ $fixture['name'] ? ' "' . $fixture['name'] . '"' : '',
+ $fixture['factory'],
+ $exception->getMessage(),
+ $exception->getTraceAsString()
+ ),
+ 0,
+ $exception
+ );
+ }
+
+ if ($result !== null && !empty($fixture['name'])) {
+ DataFixtureStorageManager::getStorage()->persist(
+ $fixture['name'],
+ $result
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Revert data fixture.
+ *
+ * @param array $fixture
+ */
+ public function revert(array $fixture): void
+ {
+ $isSecureArea = $this->registry->registry('isSecureArea');
+ $this->registry->unregister('isSecureArea');
+ $this->registry->register('isSecureArea', true);
+ try {
+ $factory = $this->dataFixtureFactory->create($fixture['factory']);
+ if ($factory instanceof RevertibleDataFixtureInterface) {
+ $factory->revert($fixture['result'] ?? new DataObject());
+ }
+ } catch (NoSuchEntityException $exception) {
+ //ignore
+ } catch (\Throwable $exception) {
+ throw new Exception(
+ sprintf(
+ "Unable to revert fixture%s: %s.\n%s\n%s",
+ $fixture['name'] ? '"' . $fixture['name'] . '"' : '',
+ $fixture['factory'],
+ $exception->getMessage(),
+ $exception->getTraceAsString()
+ ),
+ 0,
+ $exception
+ );
+ } finally {
+ $this->registry->unregister('isSecureArea');
+ $this->registry->register('isSecureArea', $isSecureArea);
+ }
+ }
+
+ /**
+ * Replace fixtures references in the data by their value
+ *
+ * Supported formats:
+ * - $fixture$
+ * - $fixture.attribute$
+ *
+ * @param array $data
+ * @return array
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function resolveVariables(array $data): array
+ {
+ foreach ($data as $key => $value) {
+ if (is_array($value)) {
+ $data[$key] = $this->resolveVariables($value);
+ } else {
+ if (is_string($value) && preg_match('/^\$\w+(\.\w+)?\$$/', $value)) {
+ list($fixtureName, $attribute) = array_pad(explode('.', trim($value, '$')), 2, null);
+ $fixtureData = DataFixtureStorageManager::getStorage()->get($fixtureName);
+ if (!$fixtureData) {
+ throw new \InvalidArgumentException("Unable to resolve fixture reference '$value'");
+ }
+ $data[$key] = $attribute ? $fixtureData->getDataUsingMethod($attribute) : $fixtureData;
+ }
+ }
+ }
+
+ return $data;
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Application.php b/dev/tests/integration/framework/Magento/TestFramework/Application.php
index 189f1bbb43c13..57c46d197c62f 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Application.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Application.php
@@ -13,6 +13,7 @@
use Magento\Framework\Filesystem\Glob;
use Magento\Framework\Mail;
use Magento\TestFramework;
+use Magento\TestFramework\Fixture\Data\ProcessorInterface;
use Psr\Log\LoggerInterface;
use DomainException;
@@ -27,7 +28,7 @@ class Application
/**
* Default application area.
*/
- const DEFAULT_APP_AREA = 'global';
+ public const DEFAULT_APP_AREA = 'global';
/**
* DB vendor adapter instance.
@@ -416,6 +417,7 @@ public function initialize($overriddenParams = [])
\Magento\Framework\App\State::class => TestFramework\App\State::class,
Mail\TransportInterface::class => TestFramework\Mail\TransportInterfaceMock::class,
Mail\Template\TransportBuilder::class => TestFramework\Mail\Template\TransportBuilderMock::class,
+ ProcessorInterface::class => \Magento\TestFramework\Fixture\Data\CompositeProcessor::class,
]
];
if ($this->loadTestExtensionAttributes) {
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php b/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php
index e95b8821deebf..7b7770f44b6c3 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php
@@ -55,6 +55,7 @@ protected function _getSubscribers(Application $application)
new \Magento\TestFramework\Workaround\Segfault(),
new \Magento\TestFramework\Workaround\Cleanup\TestCaseProperties(),
new \Magento\TestFramework\Workaround\Cleanup\StaticProperties(),
+ new \Magento\TestFramework\Isolation\FlushDataFixtureStorage(),
new \Magento\TestFramework\Isolation\WorkingDirectory(),
new \Magento\TestFramework\Isolation\DeploymentConfig(),
new \Magento\TestFramework\Workaround\Override\Fixture\Resolver\TestSetter(),
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/DataMerger.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/DataMerger.php
new file mode 100644
index 0000000000000..e06dc3b5bbd44
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/DataMerger.php
@@ -0,0 +1,129 @@
+convertCustomAttributesToMap(
+ $data[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES]
+ );
+ foreach ($data as $key => $value) {
+ if (!array_key_exists($key, $defaultData)) {
+ if (array_key_exists($key, $defaultData[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY])) {
+ $data[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY][$key] = $value;
+ } else {
+ $data[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES][$key] = $value;
+ }
+ unset($data[$key]);
+ }
+ }
+ }
+
+ $result = $this->mergeRecursive($defaultData, $data);
+
+ if ($isExtensible) {
+ $result[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] = $this->convertCustomAttributesToCollection(
+ $result[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES]
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Recursively merge entity data
+ *
+ * @param array $arrays
+ * @return array
+ */
+ public function mergeRecursive(array ...$arrays): array
+ {
+ $result = [];
+ while ($arrays) {
+ $array = array_shift($arrays);
+ // is array an associative array
+ if (array_values($array) !== $array) {
+ foreach ($array as $key => $value) {
+ if (is_array($value) && array_key_exists($key, $result) && is_array($result[$key])) {
+ $result[$key] = $this->mergeRecursive($result[$key], $value);
+ } else {
+ $result[$key] = $value;
+ }
+ }
+ } elseif (array_values($result) === $result) {
+ $result = $array;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Return an associative array with attribute codes as key
+ *
+ * @param array $data
+ * @return mixed
+ */
+ private function convertCustomAttributesToMap(array $data): array
+ {
+ $result = [];
+ // check if data is not an associative array
+ if (array_values($data) === $data) {
+ foreach ($data as $item) {
+ $result[$item[AttributeInterface::ATTRIBUTE_CODE]] = $item[AttributeInterface::VALUE];
+ }
+ } else {
+ $result = $data;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Return a multi-dimension array with attribute codes and values
+ *
+ * @param array $data
+ * @return mixed
+ */
+ private function convertCustomAttributesToCollection(array $data): array
+ {
+ $result = [];
+ foreach ($data as $key => $value) {
+ $result[] = [
+ AttributeInterface::ATTRIBUTE_CODE => $key,
+ AttributeInterface::VALUE => $value,
+ ];
+ }
+
+ return $result;
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/Service.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/Service.php
new file mode 100644
index 0000000000000..aa25237bdf594
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/Service.php
@@ -0,0 +1,73 @@
+objectManager = $objectManager;
+ $this->serviceInputProcessor = $serviceInputProcessor;
+ $this->className = $className;
+ $this->methodName = $methodName;
+ }
+
+ /**
+ * Execute the Api service with provided the data
+ *
+ * @param array $data
+ * @return mixed
+ */
+ public function execute(array $data)
+ {
+ $params = $this->serviceInputProcessor->process(
+ $this->className,
+ $this->methodName,
+ $data
+ );
+ $service = $this->objectManager->get($this->className);
+
+ return call_user_func_array([$service, $this->methodName], $params);
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/ServiceFactory.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/ServiceFactory.php
new file mode 100644
index 0000000000000..b118208f8a961
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/ServiceFactory.php
@@ -0,0 +1,48 @@
+objectManager = $objectManager;
+ }
+
+ /**
+ * Create Api service
+ *
+ * @param string $className
+ * @param string $methodName
+ * @return Service
+ */
+ public function create(string $className, string $methodName): Service
+ {
+ return $this->objectManager->create(
+ Service::class,
+ [
+ 'className' => $className,
+ 'methodName' => $methodName
+ ]
+ );
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/CallableDataFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/CallableDataFixture.php
new file mode 100644
index 0000000000000..007c17475f124
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/CallableDataFixture.php
@@ -0,0 +1,57 @@
+callback = $callback;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ call_user_func($this->callback);
+ return null;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function revert(DataObject $data): void
+ {
+ $rollbackCallback = null;
+ if (is_array($this->callback)) {
+ $rollbackCallback = $this->callback;
+ $rollbackCallback[1] .= 'Rollback';
+ } elseif (is_string($this->callback)) {
+ $rollbackCallback = $this->callback;
+ $rollbackCallback .= 'Rollback';
+ }
+ if ($rollbackCallback && is_callable($rollbackCallback)) {
+ call_user_func($rollbackCallback);
+ }
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/Data/CompositeProcessor.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Data/CompositeProcessor.php
new file mode 100644
index 0000000000000..bc91f5cce5876
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Data/CompositeProcessor.php
@@ -0,0 +1,61 @@
+objectManager = $objectManager;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function process(DataFixtureInterface $fixture, array $data): array
+ {
+ foreach ($this->getProcessors() as $processor) {
+ $data = $this->objectManager->get($processor)->process($fixture, $data);
+ }
+ return $data;
+ }
+
+ /**
+ * Get registered processors
+ *
+ * @return array
+ */
+ private function getProcessors(): array
+ {
+ return [
+ UniqueIdProcessor::class
+ ];
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/Data/ProcessorInterface.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Data/ProcessorInterface.php
new file mode 100644
index 0000000000000..6fab1a4822943
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Data/ProcessorInterface.php
@@ -0,0 +1,25 @@
+ uniqid(), 'increment' => self::INCREMENT];
+ }
+ $hash = self::$storage[$class]['prefix'] . self::$storage[$class]['increment']++;
+ array_walk_recursive($data, function (&$value) use ($hash) {
+ if (is_string($value)) {
+ $value = str_replace(self::PLACEHOLDER, $hash, $value);
+ }
+ });
+ return $data;
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/DataFixtureFactory.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/DataFixtureFactory.php
new file mode 100644
index 0000000000000..425182448dba3
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/DataFixtureFactory.php
@@ -0,0 +1,59 @@
+objectManager = $objectManager;
+ }
+
+ /**
+ * Create new instance of data fixture
+ *
+ * @param string $fixture
+ * @return DataFixtureInterface
+ */
+ public function create(string $fixture): DataFixtureInterface
+ {
+ if (is_callable($fixture)) {
+ $result = $this->objectManager->create(
+ CallableDataFixture::class,
+ [
+ 'callback' => $fixture
+ ]
+ );
+ } elseif (class_exists($fixture)) {
+ $result = $this->objectManager->create($fixture);
+ } else {
+ $result = $this->objectManager->create(
+ LegacyDataFixture::class,
+ [
+ 'filePath' => $fixture,
+ ]
+ );
+ }
+
+ return $result;
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/DataFixtureInterface.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/DataFixtureInterface.php
new file mode 100644
index 0000000000000..539e72e5b4a17
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/DataFixtureInterface.php
@@ -0,0 +1,24 @@
+fixtures[$name] ?? null;
+ }
+
+ /**
+ * Persist fixture result to the storage
+ *
+ * @param string $name
+ * @param DataObject|null $data
+ */
+ public function persist(string $name, ?DataObject $data): void
+ {
+ $this->fixtures[$name] = $data;
+ }
+
+ /**
+ * Flush the storage
+ */
+ public function flush(): void
+ {
+ $this->fixtures = [];
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/DataFixtureStorageManager.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/DataFixtureStorageManager.php
new file mode 100644
index 0000000000000..e4d5246474ebc
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/DataFixtureStorageManager.php
@@ -0,0 +1,46 @@
+filePath = $fixturePathResolver->resolve($filePath);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function apply(array $data = []): ?DataObject
+ {
+ $this->execute($this->filePath);
+ return null;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function revert(DataObject $data): void
+ {
+ $fileInfo = pathinfo($this->filePath);
+ $extension = '';
+ if (isset($fileInfo['extension'])) {
+ $extension = '.' . $fileInfo['extension'];
+ }
+ $rollbackScript = $fileInfo['dirname'] . DIRECTORY_SEPARATOR . $fileInfo['filename'] . '_rollback' . $extension;
+ if (file_exists($rollbackScript)) {
+ $this->execute($rollbackScript);
+ }
+ }
+
+ /**
+ * Execute file
+ *
+ * @param string $filePath
+ */
+ private function execute(string $filePath): void
+ {
+ try {
+ require $filePath;
+ } catch (\Throwable $e) {
+ throw new \Exception(
+ 'Error in fixture ' . $filePath . PHP_EOL . $e->getTraceAsString(),
+ 0,
+ $e
+ );
+ }
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/LegacyDataFixturePathResolver.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/LegacyDataFixturePathResolver.php
new file mode 100644
index 0000000000000..7866c41b19125
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/LegacyDataFixturePathResolver.php
@@ -0,0 +1,81 @@
+componentRegistrar = $componentRegistrar;
+ }
+
+ /**
+ * Get the full path to the fixture
+ *
+ * @param string $fixture
+ * @return string
+ * @throws LocalizedException
+ */
+ public function resolve(string $fixture): string
+ {
+ if ($this->isModuleAnnotation($fixture)) {
+ $filePath = $this->getModulePath($fixture);
+ } else {
+ $filePath = INTEGRATION_TESTS_DIR . '/testsuite/' . $fixture;
+ }
+
+ return $filePath;
+ }
+
+ /**
+ * Check if the fixture file is located in the module path
+ *
+ * @param string $fixture
+ * @return bool
+ */
+ private function isModuleAnnotation(string $fixture): bool
+ {
+ return strpos($fixture, '::') !== false;
+ }
+
+ /**
+ * Get the full path to the fixture in the module
+ *
+ * @param string $fixture
+ * @return string
+ * @throws LocalizedException
+ */
+ private function getModulePath(string $fixture): string
+ {
+ [$moduleName, $fixtureFile] = explode('::', $fixture, 2);
+
+ $modulePath = $this->componentRegistrar->getPath(ComponentRegistrar::MODULE, $moduleName);
+
+ if ($modulePath === null) {
+ throw new LocalizedException(__('Can\'t find registered Module with name %1 .', $moduleName));
+ }
+
+ return $modulePath . '/' . ltrim($fixtureFile, '/');
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/RevertibleDataFixtureInterface.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/RevertibleDataFixtureInterface.php
new file mode 100644
index 0000000000000..1902002ca5d96
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/RevertibleDataFixtureInterface.php
@@ -0,0 +1,23 @@
+flush();
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Workaround/Override/Fixture/Applier/DataFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Workaround/Override/Fixture/Applier/DataFixture.php
index efd92b46a2bf7..9528282c288b2 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Workaround/Override/Fixture/Applier/DataFixture.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Workaround/Override/Fixture/Applier/DataFixture.php
@@ -30,9 +30,9 @@ public function replace(string $fixture): string
}
}
}
- $fixture = $this->replaceFixtures([$fixture], $replacedFixtures);
+ $fixture = $this->replaceFixtures([$this->getFixtureAsArray($fixture)], $replacedFixtures);
- return is_array($fixture) ? reset($fixture) : $fixture;
+ return reset($fixture)['factory'];
}
/**
@@ -68,9 +68,11 @@ public function apply(array $fixtures): array
*/
private function replaceFixtures(array $fixtures, array $replacedFixtures): array
{
- foreach ($fixtures as $key => $fixture) {
- if (!empty($replacedFixtures[$fixture])) {
- $fixtures[$key] = $replacedFixtures[$fixture];
+ if ($replacedFixtures) {
+ foreach ($fixtures as $key => $fixture) {
+ if (!empty($replacedFixtures[$fixture['factory']])) {
+ $fixtures[$key] = $this->getFixtureAsArray($replacedFixtures[$fixture['factory']]);
+ }
}
}
@@ -86,9 +88,11 @@ private function replaceFixtures(array $fixtures, array $replacedFixtures): arra
*/
private function removeFixtures(array $fixtures, array $attributes): array
{
- $key = array_search($attributes['path'], $fixtures);
- if ($key || $key === 0) {
+ try {
+ $key = $this->getFixturePosition($attributes['path'], $fixtures);
unset($fixtures[$key]);
+ } catch (\Throwable $exception) {
+ //ignore exception
}
return $fixtures;
@@ -106,22 +110,22 @@ private function sortFixtures(array $fixtures, array $attributes): array
$beforeFixtures = [];
$afterFixtures = [];
if (!empty($attributes['before'])) {
- $offset = $this->getFixturePosition($attributes['before'], $fixtures);
- if ($attributes['before'] === '-' || $offset === 0) {
- $beforeFixtures[] = $attributes['path'];
+ $offset = $attributes['before'] === '-' ? 0 : $this->getFixturePosition($attributes['before'], $fixtures);
+ if ($offset === 0) {
+ $beforeFixtures[] = $this->getFixtureAsArray($attributes['path']);
} else {
$fixtures = $this->insertFixture($fixtures, $attributes['path'], $offset);
}
}
if (!empty($attributes['after'])) {
if ($attributes['after'] === '-') {
- $afterFixtures[] = $attributes['path'];
+ $afterFixtures[] = $this->getFixtureAsArray($attributes['path']);
} else {
$offset = $this->getFixturePosition($attributes['after'], $fixtures);
$fixtures = $this->insertFixture($fixtures, $attributes['path'], $offset + 1);
}
} elseif (empty($attributes['before'])) {
- $fixtures[] = $attributes['path'];
+ $fixtures[] = $this->getFixtureAsArray($attributes['path']);
}
return array_merge($beforeFixtures, $fixtures, $afterFixtures);
@@ -137,13 +141,16 @@ private function sortFixtures(array $fixtures, array $attributes): array
*/
private function getFixturePosition(string $fixtureToFind, array $existingFixtures): int
{
- $offset = 0;
- if ($fixtureToFind !== '-') {
- $offset = array_search($fixtureToFind, $existingFixtures);
- if ($offset === false) {
- throw new LocalizedException(__('The fixture %1 does not exist in fixtures list', $fixtureToFind));
+ $offset = false;
+ foreach ($existingFixtures as $key => $fixture) {
+ if ($fixture['factory'] === $fixtureToFind) {
+ $offset = $key;
+ break;
}
}
+ if ($offset === false) {
+ throw new LocalizedException(__('The fixture %1 does not exist in fixtures list', $fixtureToFind));
+ }
return $offset;
}
@@ -160,8 +167,21 @@ private function insertFixture(array $fixtures, string $fixture, int $position):
{
return array_merge(
array_slice($fixtures, 0, $position),
- [$fixture],
+ [$this->getFixtureAsArray($fixture)],
array_slice($fixtures, $position)
);
}
+
+ /**
+ * Creates an array with the supplied fixture factory
+ *
+ * @param string $fixture
+ * @return string[]
+ */
+ private function getFixtureAsArray(string $fixture): array
+ {
+ return [
+ 'factory' => $fixture
+ ];
+ }
}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Workaround/Override/Fixture/Resolver.php b/dev/tests/integration/framework/Magento/TestFramework/Workaround/Override/Fixture/Resolver.php
index 33bf1011c5b7b..f7bbfbf326cae 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Workaround/Override/Fixture/Resolver.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Workaround/Override/Fixture/Resolver.php
@@ -7,14 +7,13 @@
namespace Magento\TestFramework\Workaround\Override\Fixture;
-use Magento\Framework\Component\ComponentRegistrar;
-use Magento\Framework\Component\ComponentRegistrarInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\ObjectManagerInterface;
use Magento\TestFramework\Annotation\AdminConfigFixture;
use Magento\TestFramework\Annotation\ConfigFixture;
use Magento\TestFramework\Annotation\DataFixture;
use Magento\TestFramework\Annotation\DataFixtureBeforeTransaction;
+use Magento\TestFramework\Annotation\DataFixtureSetup;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Workaround\Override\ConfigInterface;
use Magento\TestFramework\Workaround\Override\Fixture\Applier\AdminConfigFixture as AdminConfigFixtureApplier;
@@ -49,6 +48,11 @@ class Resolver implements ResolverInterface
/** @var string */
private $currentFixtureType = null;
+ /**
+ * @var DataFixtureSetup
+ */
+ private $dataFixtureSetup;
+
/**
* @param ConfigInterface $config
*/
@@ -56,6 +60,7 @@ public function __construct(ConfigInterface $config)
{
$this->config = $config;
$this->objectManager = Bootstrap::getObjectManager();
+ $this->dataFixtureSetup = $this->objectManager->create(DataFixtureSetup::class);
}
/**
@@ -117,9 +122,7 @@ public function requireDataFixture(string $path): void
}
/** @var DataFixtureApplier $dataFixtureApplier */
$dataFixtureApplier = $this->getApplier($this->getCurrentTest(), $this->currentFixtureType);
- $fixture = $this->processFixturePath($this->currentTest, $dataFixtureApplier->replace($path));
-
- is_callable($fixture) ? call_user_func($fixture) : require $fixture;
+ $this->dataFixtureSetup->apply(['factory' => $dataFixtureApplier->replace($path)]);
}
/**
@@ -143,11 +146,7 @@ public function applyDataFixtures(TestCase $test, array $fixtures, string $fixtu
$skipConfig = $this->config->getSkipConfiguration($test);
if (!$skipConfig['skip']) {
- $fixtures = $this->getApplier($test, $fixtureType)->apply($fixtures);
-
- foreach ($fixtures as $fixture) {
- $result[] = $this->processFixturePath($test, $fixture);
- }
+ $result = $this->getApplier($test, $fixtureType)->apply($fixtures);
}
return $result;
@@ -179,16 +178,6 @@ protected function getApplierByFixtureType(string $fixtureType): ApplierInterfac
return $applier;
}
- /**
- * Get ComponentRegistrar object
- *
- * @return ComponentRegistrarInterface
- */
- protected function getComponentRegistrar(): ComponentRegistrarInterface
- {
- return $this->objectManager->get(ComponentRegistrar::class);
- }
-
/**
* Get applier with prepared config by annotation type
*
@@ -214,64 +203,4 @@ private function getApplier(TestCase $test, string $fixtureType): ApplierInterfa
return $applier;
}
-
- /**
- * Converts fixture path.
- *
- * @param TestCase $test
- * @param string $fixture
- * @return string|array
- * @throws LocalizedException
- */
- private function processFixturePath(TestCase $test, string $fixture)
- {
- if (strpos($fixture, '\\') !== false) {
- // usage of a single directory separator symbol streamlines search across the source code
- throw new LocalizedException(__('Directory separator "\\" is prohibited in fixture declaration.'));
- }
-
- $fixtureMethod = [get_class($test), $fixture];
- if (is_callable($fixtureMethod)) {
- $result = $fixtureMethod;
- } elseif ($this->isModuleAnnotation($fixture)) {
- $result = $this->getModulePath($fixture);
- } else {
- $result = INTEGRATION_TESTS_DIR . '/testsuite/' . $fixture;
- }
-
- return $result;
- }
-
- /**
- * Check is the Annotation like Magento_InventoryApi::Test/_files/products.php
- *
- * @param string $fixture
- * @return bool
- */
- private function isModuleAnnotation(string $fixture): bool
- {
- return (strpos($fixture, '::') !== false);
- }
-
- /**
- * Resolve the fixture module annotation path.
- *
- * @param string $fixture
- * @return string
- * @throws LocalizedException
- * @SuppressWarnings(PHPMD.StaticAccess)
- */
- private function getModulePath(string $fixture): string
- {
- $componentRegistrar = $this->getComponentRegistrar();
- [$moduleName, $fixtureFile] = explode('::', $fixture, 2);
-
- $modulePath = $componentRegistrar->getPath(ComponentRegistrar::MODULE, $moduleName);
-
- if ($modulePath === null) {
- throw new LocalizedException(__('Can\'t find registered Module with name %1 .', $moduleName));
- }
-
- return $modulePath . '/' . ltrim($fixtureFile, '/');
- }
}
diff --git a/dev/tests/integration/framework/bootstrap.php b/dev/tests/integration/framework/bootstrap.php
index f46ec4738fd7d..75809e5dcd153 100644
--- a/dev/tests/integration/framework/bootstrap.php
+++ b/dev/tests/integration/framework/bootstrap.php
@@ -115,6 +115,9 @@
Magento\TestFramework\Workaround\Override\Fixture\Resolver::setInstance(
new \Magento\TestFramework\Workaround\Override\Fixture\Resolver($overrideConfig)
);
+ Magento\TestFramework\Fixture\DataFixtureStorageManager::setStorage(
+ new Magento\TestFramework\Fixture\DataFixtureStorage()
+ );
/* Unset declared global variables to release the PHPUnit from maintaining their values between tests */
unset($testsBaseDir, $settings, $shell, $application, $bootstrap, $overrideConfig);
} catch (\Exception $e) {
diff --git a/dev/tests/integration/framework/tests/unit/phpunit.xml.dist b/dev/tests/integration/framework/tests/unit/phpunit.xml.dist
index b9d3b513bcfb0..f411244be86f9 100644
--- a/dev/tests/integration/framework/tests/unit/phpunit.xml.dist
+++ b/dev/tests/integration/framework/tests/unit/phpunit.xml.dist
@@ -38,12 +38,15 @@
magentoConfigFixture
-
+
magentoDataFixture
magentoDbIsolation
+
+ magentoDataFixtureDataProvider
+
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureDirectivesParserTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureDirectivesParserTest.php
new file mode 100644
index 0000000000000..a2b213aed7db9
--- /dev/null
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureDirectivesParserTest.php
@@ -0,0 +1,105 @@
+model = new DataFixtureDirectivesParser(new Json());
+ }
+
+ /**
+ * Test parse with different format
+ *
+ * @param string $directive
+ * @dataProvider directivesDataProvider
+ */
+ public function testParse(string $directive, array $expected): void
+ {
+ $this->assertEquals($expected, $this->model->parse($directive));
+ }
+
+ /**
+ * Test parse with invalid json
+ */
+ public function testParseInvalidJson(): void
+ {
+ $this->expectExceptionMessage('Unable to unserialize value. Error: Syntax error');
+ $this->model->parse('path/to/fixture.php as:test1 with:{"k1": "v1" "k2": ["v21", "v22"]}');
+ }
+
+ /**
+ * @return array
+ */
+ public function directivesDataProvider(): array
+ {
+ return [
+ [
+ 'path/to/fixture.php as:test1 with:{"k1": "v1", "k2": ["v21", "v22"], "k3": {"k 31": "v 31"}}',
+ [
+ 'name' => 'test1',
+ 'factory' => 'path/to/fixture.php',
+ 'data' => [
+ 'k1' => 'v1',
+ 'k2' => ['v21', 'v22'],
+ 'k3' => ['k 31' => 'v 31'],
+ ],
+ ]
+ ],
+ [
+ 'path/to/fixture.php with:{"k1": "v1", "k2": ["v21", "v22"], "k3": {"k 31": "v 31"}} as:test1',
+ [
+ 'name' => 'test1',
+ 'factory' => 'path/to/fixture.php',
+ 'data' => [
+ 'k1' => 'v1',
+ 'k2' => ['v21', 'v22'],
+ 'k3' => ['k 31' => 'v 31'],
+ ],
+ ]
+ ],
+ [
+ 'path/to/fixture.php with:{"k1": "v1", "k2": ["v21", "v22"], "k3": {"k 31": "v 31"}}',
+ [
+ 'name' => null,
+ 'factory' => 'path/to/fixture.php',
+ 'data' => [
+ 'k1' => 'v1',
+ 'k2' => ['v21', 'v22'],
+ 'k3' => ['k 31' => 'v 31'],
+ ],
+ ]
+ ],
+ [
+ 'path/to/fixture.php as:test1',
+ [
+ 'name' => 'test1',
+ 'factory' => 'path/to/fixture.php',
+ 'data' => [],
+ ]
+ ],
+ ];
+ }
+}
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php
index d95b3270384df..61b39b04520d6 100644
--- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php
@@ -8,55 +8,143 @@
namespace Magento\Test\Annotation;
use Magento\Framework\Component\ComponentRegistrar;
+use Magento\Framework\DataObject;
use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\Registry;
+use Magento\Framework\Serialize\Serializer\Json;
use Magento\TestFramework\Annotation\DataFixture;
+use Magento\TestFramework\Annotation\DataFixtureDataProvider;
+use Magento\TestFramework\Annotation\DataFixtureSetup;
use Magento\TestFramework\Event\Param\Transaction;
+use Magento\TestFramework\Annotation\DataFixtureDirectivesParser;
+use Magento\TestFramework\Fixture\DataFixtureInterface;
+use Magento\TestFramework\Fixture\DataFixtureStorage;
+use Magento\TestFramework\Fixture\DataFixtureStorageManager;
+use Magento\TestFramework\Fixture\LegacyDataFixturePathResolver;
+use Magento\TestFramework\Fixture\DataFixtureFactory;
+use Magento\TestFramework\Fixture\LegacyDataFixture;
+use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;
+use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Magento\TestFramework\Annotation\TestsIsolation;
+use ReflectionException;
/**
* Test class for \Magento\TestFramework\Annotation\DataFixture.
*
* @magentoDataFixture sampleFixtureOne
+ * @magentoDataFixtureDataProvider classFixtureDataProvider
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class DataFixtureTest extends TestCase
{
/**
- * @var DataFixture|\PHPUnit\Framework\MockObject\MockObject
+ * @var DataFixture|MockObject
*/
protected $object;
/**
- * @var TestsIsolation|\PHPUnit\Framework\MockObject\MockObject
+ * @var TestsIsolation|MockObject
*/
protected $testsIsolationMock;
+ /**
+ * @var RevertibleDataFixtureInterface|MockObject
+ */
+ private $fixture1;
+
+ /**
+ * @var RevertibleDataFixtureInterface|MockObject
+ */
+ private $fixture2;
+
+ /**
+ * @var DataFixtureInterface|MockObject
+ */
+ private $fixture3;
+
+ /**
+ * @var DataObject
+ */
+ private $fixtureStorage;
+
/**
* @inheritdoc
*/
protected function setUp(): void
{
- $this->object = $this->getMockBuilder(DataFixture::class)
- ->onlyMethods(['_applyOneFixture', 'getTestKey'])
- ->addMethods(['getComponentRegistrar'])
- ->getMock();
+ $this->object = new DataFixture();
$this->testsIsolationMock = $this->getMockBuilder(TestsIsolation::class)
->onlyMethods(['createDbSnapshot', 'checkTestIsolation'])
->getMock();
- /** @var ObjectManagerInterface|\PHPUnit\Framework\MockObject\MockObject $objectManager */
+ /** @var ObjectManagerInterface|MockObject $objectManager */
$objectManager = $this->getMockBuilder(ObjectManagerInterface::class)
- ->onlyMethods(['get'])
+ ->onlyMethods(['get', 'create'])
->disableOriginalConstructor()
->getMockForAbstractClass();
- $objectManager->expects($this->atLeastOnce())->method('get')->with(TestsIsolation::class)
- ->willReturn($this->testsIsolationMock);
- \Magento\TestFramework\Helper\Bootstrap::setObjectManager($objectManager);
+
+ $this->fixture1 = $this->getMockBuilder(RevertibleDataFixtureInterface::class)
+ ->setMockClassName('MockFixture1')
+ ->getMockForAbstractClass();
+ $this->fixture2 = $this->getMockBuilder(RevertibleDataFixtureInterface::class)
+ ->setMockClassName('MockFixture2')
+ ->getMockForAbstractClass();
+ $this->fixture3 = $this->getMockBuilder(DataFixtureInterface::class)
+ ->setMockClassName('MockFixture3')
+ ->getMockForAbstractClass();
+
+ $this->fixtureStorage = new DataFixtureStorage();
+ DataFixtureStorageManager::setStorage($this->fixtureStorage);
+
+ $dataFixtureFactory = new DataFixtureFactory($objectManager);
+
+ $sharedInstances = [
+ TestsIsolation::class => $this->testsIsolationMock,
+ DataFixtureDirectivesParser::class => new DataFixtureDirectivesParser(new Json()),
+ DataFixtureFactory::class => $dataFixtureFactory,
+ DataFixtureSetup::class => new DataFixtureSetup(new Registry(), $dataFixtureFactory),
+ DataFixtureDataProvider::class => new DataFixtureDataProvider(new Json()),
+ 'MockFixture1' => $this->fixture1,
+ 'MockFixture2' => $this->fixture2,
+ 'MockFixture3' => $this->fixture3,
+ ];
+ $objectManager->expects($this->atLeastOnce())
+ ->method('get')
+ ->willReturnCallback(
+ function (string $type) use ($sharedInstances) {
+ return $sharedInstances[$type] ?? new $type();
+ }
+ );
+ $objectManager->expects($this->atLeastOnce())
+ ->method('create')
+ ->willReturnCallback(
+ function (string $type, array $arguments = []) use ($sharedInstances) {
+ if ($type === LegacyDataFixture::class) {
+ array_unshift($arguments, new LegacyDataFixturePathResolver(new ComponentRegistrar()));
+ }
+ return $sharedInstances[$type] ?? new $type(...array_values($arguments));
+ }
+ );
+ Bootstrap::setObjectManager($objectManager);
$directory = __DIR__;
if (!defined('INTEGRATION_TESTS_DIR')) {
define('INTEGRATION_TESTS_DIR', dirname($directory, 4));
}
+
+ $this->createResolverMock();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ putenv('sample_fixture_one');
+ putenv('sample_fixture_two');
+ putenv('sample_fixture_three');
}
/**
@@ -66,6 +154,7 @@ protected function setUp(): void
*/
public static function sampleFixtureOne(): void
{
+ putenv('sample_fixture_one=1');
}
/**
@@ -75,6 +164,7 @@ public static function sampleFixtureOne(): void
*/
public static function sampleFixtureTwo(): void
{
+ putenv('sample_fixture_two=2');
}
/**
@@ -84,6 +174,7 @@ public static function sampleFixtureTwo(): void
*/
public static function sampleFixtureTwoRollback(): void
{
+ putenv('sample_fixture_two');
}
/**
@@ -91,7 +182,6 @@ public static function sampleFixtureTwoRollback(): void
*/
public function testStartTestTransactionRequestClassAnnotation(): void
{
- $this->createResolverMock();
$eventParam = new Transaction();
$this->object->startTestTransactionRequest($this, $eventParam);
$this->assertTrue($eventParam->isTransactionStartRequested());
@@ -105,13 +195,12 @@ public function testStartTestTransactionRequestClassAnnotation(): void
/**
* @magentoDataFixture sampleFixtureTwo
- * @magentoDataFixture path/to/fixture/script.php
+ * @magentoDataFixture Magento/Test/Annotation/_files/sample_fixture_three.php
*
* @return void
*/
public function testStartTestTransactionRequestMethodAnnotation(): void
{
- $this->createResolverMock();
$eventParam = new Transaction();
$this->object->startTestTransactionRequest($this, $eventParam);
$this->assertTrue($eventParam->isTransactionStartRequested());
@@ -127,13 +216,12 @@ public function testStartTestTransactionRequestMethodAnnotation(): void
/**
* @magentoDbIsolation disabled
* @magentoDataFixture sampleFixtureTwo
- * @magentoDataFixture path/to/fixture/script.php
+ * @magentoDataFixture Magento/Test/Annotation/_files/sample_fixture_three.php
*
* @return void
*/
public function testDisabledDbIsolation(): void
{
- $this->createResolverMock();
$eventParam = new Transaction();
$this->object->startTestTransactionRequest($this, $eventParam);
$this->assertFalse($eventParam->isTransactionStartRequested());
@@ -148,13 +236,12 @@ public function testDisabledDbIsolation(): void
/**
* @magentoDataFixture sampleFixtureTwo
- * @magentoDataFixture path/to/fixture/script.php
+ * @magentoDataFixture Magento/Test/Annotation/_files/sample_fixture_three.php
*
* @return void
*/
public function testEndTestTransactionRequestMethodAnnotation(): void
{
- $this->createResolverMock();
$eventParam = new Transaction();
$this->object->endTestTransactionRequest($this, $eventParam);
$this->assertFalse($eventParam->isTransactionStartRequested());
@@ -172,30 +259,21 @@ public function testEndTestTransactionRequestMethodAnnotation(): void
*/
public function testStartTransactionClassAnnotation(): void
{
- $this->createResolverMock();
- $this->object->expects($this->once())
- ->method('_applyOneFixture')
- ->with([__CLASS__, 'sampleFixtureOne']);
$this->object->startTransaction($this);
+ $this->assertEquals('1', getenv('sample_fixture_one'));
}
/**
* @magentoDataFixture sampleFixtureTwo
- * @magentoDataFixture path/to/fixture/script.php
+ * @magentoDataFixture Magento/Test/Annotation/_files/sample_fixture_three.php
*
* @return void
*/
public function testStartTransactionMethodAnnotation(): void
{
- $this->createResolverMock();
- $this->object
- ->method('_applyOneFixture')
- ->withConsecutive(
- [[__CLASS__, 'sampleFixtureTwo']],
- [$this->stringEndsWith('path/to/fixture/script.php')]
- );
-
$this->object->startTransaction($this);
+ $this->assertEquals('2', getenv('sample_fixture_two'));
+ $this->assertEquals('3', getenv('sample_fixture_three'));
}
/**
@@ -206,40 +284,29 @@ public function testStartTransactionMethodAnnotation(): void
*/
public function testRollbackTransactionRevertFixtureMethod(): void
{
- $this->createResolverMock();
$this->object->startTransaction($this);
- $this->object->expects(
- $this->once()
- )->method(
- '_applyOneFixture'
- )->with(
- [__CLASS__, 'sampleFixtureTwoRollback']
- );
+ $this->assertEquals('1', getenv('sample_fixture_one'));
+ $this->assertEquals('2', getenv('sample_fixture_two'));
$this->object->rollbackTransaction();
+ $this->assertEquals('1', getenv('sample_fixture_one'));
+ $this->assertFalse(getenv('sample_fixture_two'));
}
/**
- * @magentoDataFixture path/to/fixture/script.php
- * @magentoDataFixture Magento/Test/Annotation/_files/sample_fixture_two.php
+ * @magentoDataFixture Magento/Test/Annotation/_files/sample_fixture_three.php
*
* @return void
*/
public function testRollbackTransactionRevertFixtureFile(): void
{
- $this->createResolverMock();
$this->object->startTransaction($this);
- $this->object->expects(
- $this->once()
- )->method(
- '_applyOneFixture'
- )->with(
- $this->stringEndsWith('sample_fixture_two_rollback.php')
- );
+ $this->assertEquals('3', getenv('sample_fixture_three'));
$this->object->rollbackTransaction();
+ $this->assertFalse(getenv('sample_fixture_three'));
}
/**
- * @magentoDataFixture Foo_DataFixtureDummy::Test/Integration/foo.php
+ * @magentoDataFixture Foo_DataFixtureDummy::Annotation/_files/sample_fixture_three.php
*
* @return void
* @SuppressWarnings(PHPMD.StaticAccess)
@@ -249,12 +316,299 @@ public function testModuleDataFixture(): void
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Foo_DataFixtureDummy',
- __DIR__
+ dirname(__DIR__)
);
- $this->createResolverMock();
- $this->object->expects($this->once())->method('_applyOneFixture')
- ->with(__DIR__ . '/Test/Integration/foo.php');
$this->object->startTransaction($this);
+ $this->assertEquals('3', getenv('sample_fixture_three'));
+ }
+
+ /**
+ * @magentoDataFixture MockFixture1
+ * @magentoDataFixture MockFixture2
+ * @magentoDataFixture MockFixture3
+ * @magentoDbIsolation disabled
+ */
+ public function testFixtureClass(): void
+ {
+ $fixture1 = new DataObject();
+ $fixture2 = new DataObject();
+ $this->fixture1->expects($this->once())
+ ->method('apply')
+ ->with([])
+ ->willReturn($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('apply')
+ ->with([])
+ ->willReturn($fixture2);
+ $this->fixture3->expects($this->once())
+ ->method('apply')
+ ->with([]);
+ $this->applyFixtures();
+ $this->fixture1->expects($this->once())
+ ->method('revert')
+ ->with($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('revert')
+ ->with($fixture2);
+ $this->revertFixtures();
+ }
+
+ /**
+ * @magentoDataFixture MockFixture1 with:{"key1": "value1"}
+ * @magentoDataFixture MockFixture2 with:{"key2": "value2"}
+ * @magentoDataFixture MockFixture3 with:{"key3": "value3"}
+ * @magentoDbIsolation disabled
+ */
+ public function testFixtureClassWithParameters(): void
+ {
+ $fixture1 = new DataObject();
+ $fixture2 = new DataObject();
+ $this->fixture1->expects($this->once())
+ ->method('apply')
+ ->with(['key1' => 'value1'])
+ ->willReturn($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('apply')
+ ->with(['key2' => 'value2'])
+ ->willReturn($fixture2);
+ $this->fixture3->expects($this->once())
+ ->method('apply')
+ ->with(['key3' => 'value3']);
+ $this->applyFixtures();
+ $this->fixture1->expects($this->once())
+ ->method('revert')
+ ->with($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('revert')
+ ->with($fixture2);
+ $this->revertFixtures();
+ }
+
+ /**
+ * @magentoDataFixture MockFixture1 with:{"alias-key1": "alias-value1"} as:fixture1
+ * @magentoDataFixture MockFixture2 with:{"alias-key2": "alias-value2"} as:fixture2
+ * @magentoDataFixture MockFixture3 with:{"alias-key3": "alias-value3"} as:fixture3
+ * @magentoDbIsolation disabled
+ */
+ public function testFixtureClassWithParametersAndAlias(): void
+ {
+ $fixture1 = new DataObject();
+ $fixture2 = new DataObject();
+ $this->fixture1->expects($this->once())
+ ->method('apply')
+ ->with(['alias-key1' => 'alias-value1'])
+ ->willReturn($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('apply')
+ ->with(['alias-key2' => 'alias-value2'])
+ ->willReturn($fixture2);
+ $this->fixture3->expects($this->once())
+ ->method('apply')
+ ->with(['alias-key3' => 'alias-value3']);
+ $this->applyFixtures();
+ $this->assertSame($fixture1, $this->fixtureStorage->get('fixture1'));
+ $this->assertSame($fixture2, $this->fixtureStorage->get('fixture2'));
+ $this->assertNull($this->fixtureStorage->get('fixture3'));
+ $this->fixture1->expects($this->once())
+ ->method('revert')
+ ->with($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('revert')
+ ->with($fixture2);
+ $this->revertFixtures();
+ }
+
+ /**
+ * @magentoDataFixture MockFixture1 as:fixture1
+ * @magentoDataFixture MockFixture2 as:fixture2
+ * @magentoDataFixture MockFixture3 as:fixture3
+ * @magentoDataFixtureDataProvider methodFixtureDataProvider
+ * @magentoDbIsolation disabled
+ */
+ public function testMethodFixtureDataProvider(): void
+ {
+ $fixture1 = new DataObject();
+ $fixture2 = new DataObject();
+ $this->fixture1->expects($this->once())
+ ->method('apply')
+ ->with(['method-key1' => 'method-value1'])
+ ->willReturn($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('apply')
+ ->with(['method-key2' => 'method-value2'])
+ ->willReturn($fixture2);
+ $this->fixture3->expects($this->once())
+ ->method('apply')
+ ->with(['method-key3' => 'method-value3']);
+ $this->applyFixtures();
+ $this->assertSame($fixture1, $this->fixtureStorage->get('fixture1'));
+ $this->assertSame($fixture2, $this->fixtureStorage->get('fixture2'));
+ $this->assertNull($this->fixtureStorage->get('fixture3'));
+ $this->fixture1->expects($this->once())
+ ->method('revert')
+ ->with($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('revert')
+ ->with($fixture2);
+ $this->revertFixtures();
+ }
+
+ /**
+ * @return array
+ */
+ public function methodFixtureDataProvider(): array
+ {
+ return [
+ 'fixture1' => [
+ 'method-key1' => 'method-value1',
+ ],
+ 'fixture2' => [
+ 'method-key2' => 'method-value2',
+ ],
+ 'fixture3' => [
+ 'method-key3' => 'method-value3',
+ ],
+ ];
+ }
+
+ /**
+ * @magentoDataFixture MockFixture1 as:fixture1
+ * @magentoDataFixture MockFixture2 as:fixture2
+ * @magentoDataFixture MockFixture3 as:fixture3
+ * @magentoDbIsolation disabled
+ */
+ public function testClassFixtureDataProvider(): void
+ {
+ $fixture1 = new DataObject();
+ $fixture2 = new DataObject();
+ $this->fixture1->expects($this->once())
+ ->method('apply')
+ ->with(['class-key1' => 'class-value1'])
+ ->willReturn($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('apply')
+ ->with(['class-key2' => 'class-value2'])
+ ->willReturn($fixture2);
+ $this->fixture3->expects($this->once())
+ ->method('apply')
+ ->with(['class-key3' => 'class-value3']);
+ $this->applyFixtures();
+ $this->assertSame($fixture1, $this->fixtureStorage->get('fixture1'));
+ $this->assertSame($fixture2, $this->fixtureStorage->get('fixture2'));
+ $this->assertNull($this->fixtureStorage->get('fixture3'));
+ $this->fixture1->expects($this->once())
+ ->method('revert')
+ ->with($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('revert')
+ ->with($fixture2);
+ $this->revertFixtures();
+ }
+
+ /**
+ * @return array
+ */
+ public function classFixtureDataProvider(): array
+ {
+ return [
+ 'fixture1' => [
+ 'class-key1' => 'class-value1',
+ ],
+ 'fixture2' => [
+ 'class-key2' => 'class-value2',
+ ],
+ 'fixture3' => [
+ 'class-key3' => 'class-value3',
+ ],
+ ];
+ }
+
+ /**
+ * @magentoDataFixture MockFixture1 as:fixture1
+ * @magentoDataFixture MockFixture2 as:fixture2
+ * @magentoDataFixture MockFixture3 as:fixture3
+ * @magentoDbIsolation disabled
+ * @magentoDataFixtureDataProvider {"fixture1":{"inline-key1":"inline-value1"}}
+ * @magentoDataFixtureDataProvider {"fixture2":{"inline-key2":"inline-value2"}}
+ * @magentoDataFixtureDataProvider {"fixture3":{"inline-key3":"inline-value3"}}
+ */
+ public function testInlineFixtureDataProvider(): void
+ {
+ $fixture1 = new DataObject();
+ $fixture2 = new DataObject();
+ $this->fixture1->expects($this->once())
+ ->method('apply')
+ ->with(['inline-key1' => 'inline-value1'])
+ ->willReturn($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('apply')
+ ->with(['inline-key2' => 'inline-value2'])
+ ->willReturn($fixture2);
+ $this->fixture3->expects($this->once())
+ ->method('apply')
+ ->with(['inline-key3' => 'inline-value3']);
+ $this->applyFixtures();
+ $this->assertSame($fixture1, $this->fixtureStorage->get('fixture1'));
+ $this->assertSame($fixture2, $this->fixtureStorage->get('fixture2'));
+ $this->assertNull($this->fixtureStorage->get('fixture3'));
+ $this->fixture1->expects($this->once())
+ ->method('revert')
+ ->with($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('revert')
+ ->with($fixture2);
+ $this->revertFixtures();
+ }
+
+ /**
+ * @magentoDataFixture MockFixture1 with:{"p1": "param-value1"} as:fixture1
+ * @magentoDataFixture MockFixture2 with:{"p2": "$fixture1.attr_1$"} as:fixture2
+ * @magentoDataFixture MockFixture3 with:{"p3": "$fixture2.attr_3$", "p4": {"p5": "$fixture1$" }} as:fixture3
+ * @magentoDbIsolation disabled
+ */
+ public function testVariables(): void
+ {
+ $fixture1 = new DataObject(['attr_1' => 'attr-value1', 'attr_2' => 'attr-value2']);
+ $fixture2 = new DataObject(['attr_3' => 1]);
+ $this->fixture1->expects($this->once())
+ ->method('apply')
+ ->with(['p1' => 'param-value1'])
+ ->willReturn($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('apply')
+ ->with(['p2' => 'attr-value1'])
+ ->willReturn($fixture2);
+ $this->fixture3->expects($this->once())
+ ->method('apply')
+ ->with(['p3' => 1, 'p4' => ['p5' => $fixture1]]);
+ $this->applyFixtures();
+ $this->assertSame($fixture1, $this->fixtureStorage->get('fixture1'));
+ $this->assertSame($fixture2, $this->fixtureStorage->get('fixture2'));
+ $this->assertNull($this->fixtureStorage->get('fixture3'));
+ $this->fixture1->expects($this->once())
+ ->method('revert')
+ ->with($fixture1);
+ $this->fixture2->expects($this->once())
+ ->method('revert')
+ ->with($fixture2);
+ $this->revertFixtures();
+ }
+
+ /**
+ * @throws ReflectionException
+ */
+ private function applyFixtures(): void
+ {
+ $this->object->startTransaction($this);
+ }
+
+ /**
+ * @return void
+ */
+ private function revertFixtures(): void
+ {
+ $eventParam = new Transaction();
+ $this->object->endTestTransactionRequest($this, $eventParam);
}
/**
@@ -267,36 +621,13 @@ private function createResolverMock(): void
{
$mock = $this->getMockBuilder(Resolver::class)
->disableOriginalConstructor()
- ->onlyMethods(['applyDataFixtures', 'getComponentRegistrar'])
+ ->onlyMethods(['applyDataFixtures'])
->getMock();
- $mock->expects($this->any())->method('getComponentRegistrar')
- ->willReturn(new ComponentRegistrar());
$reflection = new \ReflectionClass(Resolver::class);
$reflectionProperty = $reflection->getProperty('instance');
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue(Resolver::class, $mock);
- $reflectionMethod = $reflection->getMethod('processFixturePath');
- $reflectionMethod->setAccessible(true);
- $annotatedFixtures = $this->getFixturesAnnotations();
- $resolvedFixtures = [];
- foreach ($annotatedFixtures as $fixture) {
- $resolvedFixtures[] = $reflectionMethod->invoke($mock, $this, $fixture);
- }
$mock->method('applyDataFixtures')
- ->willReturn($resolvedFixtures);
- }
-
- /**
- * Prepare mock method return value
- *
- * @return array
- */
- private function getFixturesAnnotations(): array
- {
- $reflection = new \ReflectionClass(DataFixture::class);
- $reflectionMethod = $reflection->getMethod('getAnnotations');
- $reflectionMethod->setAccessible(true);
-
- return $reflectionMethod->invoke($this->object, $this)['magentoDataFixture'];
+ ->willReturnArgument(1);
}
}
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/sample_fixture_three.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/sample_fixture_three.php
new file mode 100644
index 0000000000000..d93bf932e3b5c
--- /dev/null
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/sample_fixture_three.php
@@ -0,0 +1,8 @@
+createMock(ObjectManagerInterface::class);
+ $serviceInputProcessor = $this->createMock(ServiceInputProcessor::class);
+ $serviceInputProcessor->expects($this->once())
+ ->method('process')
+ ->willReturnCallback(
+ function (string $serviceClassName, string $serviceMethodName, array $params) {
+ return array_values($params);
+ }
+ );
+
+ $this->fakeClass = $this->getMockBuilder(stdClass::class)
+ ->addMethods(['fakeMethod'])
+ ->getMock();
+
+ $objectManager->expects($this->once())
+ ->method('get')
+ ->willReturn($this->fakeClass);
+
+ $this->model = new Service(
+ $objectManager,
+ $serviceInputProcessor,
+ get_class($this->fakeClass),
+ 'fakeMethod'
+ );
+ }
+
+ /**
+ * Test that the service method is executed with correct parameters
+ */
+ public function testExecute(): void
+ {
+ $params = ['param1' => 'test1', 'param2' => 'test2'];
+ $this->fakeClass->expects($this->once())
+ ->method('fakeMethod')
+ ->with('test1', 'test2');
+
+ $this->model->execute($params);
+ }
+}
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/CallableDataFixtureTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/CallableDataFixtureTest.php
new file mode 100644
index 0000000000000..66f15db66697f
--- /dev/null
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/CallableDataFixtureTest.php
@@ -0,0 +1,103 @@
+fakeClass = $this->getMockBuilder(stdClass::class)
+ ->addMethods(['fakeMethod', 'fakeMethodRollback'])
+ ->getMock();
+ }
+
+ /**
+ * @ingeritdoc
+ */
+ protected function tearDown(): void
+ {
+ static::$testFlag = '';
+ parent::tearDown();
+ }
+
+ /**
+ * Test apply with callable array
+ */
+ public function testApplyCallableArray(): void
+ {
+ $model = new CallableDataFixture([$this->fakeClass, 'fakeMethod']);
+ $this->fakeClass->expects($this->once())
+ ->method('fakeMethod');
+ $model->apply();
+ }
+
+ /**
+ * Test revert with callable array
+ */
+ public function testRevertCallableArray(): void
+ {
+ $model = new CallableDataFixture([$this->fakeClass, 'fakeMethod']);
+ $this->fakeClass->expects($this->once())
+ ->method('fakeMethodRollback');
+ $model->revert(new DataObject());
+ }
+
+ /**
+ * Test apply with callable string
+ */
+ public function testApplyCallableString(): void
+ {
+ $model = new CallableDataFixture(get_class($this) . '::fixtureMethod');
+ $model->apply();
+ $this->assertEquals('applied', static::$testFlag);
+ }
+
+ /**
+ * Test revert with callable string
+ */
+ public function testRevertCallableString(): void
+ {
+ $model = new CallableDataFixture(get_class($this) . '::fixtureMethod');
+ $model->revert(new DataObject());
+ $this->assertEquals('reverted', static::$testFlag);
+ }
+
+ public static function fixtureMethod(): void
+ {
+ static::$testFlag = 'applied';
+ }
+
+ public static function fixtureMethodRollback(): void
+ {
+ static::$testFlag = 'reverted';
+ }
+}
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/DataFixtureStorageTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/DataFixtureStorageTest.php
new file mode 100644
index 0000000000000..fc53d4827baec
--- /dev/null
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/DataFixtureStorageTest.php
@@ -0,0 +1,32 @@
+persist('fixture1', $result);
+ $this->assertSame($result, $model->get('fixture1'));
+ $this->assertNull($model->get('fixture2'));
+ $model->flush();
+ $this->assertNull($model->get('fixture1'));
+ }
+}
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/FactoryTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/FactoryTest.php
new file mode 100644
index 0000000000000..87e8200636dff
--- /dev/null
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/FactoryTest.php
@@ -0,0 +1,81 @@
+createMock(ObjectManagerInterface::class);
+ $objectManager->method('create')
+ ->willReturnCallback([$this, 'createFixture']);
+ $this->model = new DataFixtureFactory($objectManager);
+ }
+
+ /**
+ * Test that callable data fixture is created
+ */
+ public function testShouldCreateCallableDataFixture(): void
+ {
+ $this->assertInstanceOf(
+ CallableDataFixture::class,
+ $this->model->create(get_class($this) . '::' . 'tearDownAfterClass')
+ );
+ }
+
+ /**
+ * Test that legacy data fixture is created
+ */
+ public function testShouldCreateLegacyDataFixture(): void
+ {
+ $this->assertInstanceOf(LegacyDataFixture::class, $this->model->create('path/to/fixture.php'));
+ }
+
+ /**
+ * Test that class based data fixture is created
+ */
+ public function testShouldCreateDataFixture(): void
+ {
+ $this->assertInstanceOf(
+ RevertibleDataFixtureInterface::class,
+ $this->model->create(RevertibleDataFixtureInterface::class)
+ );
+ }
+
+ /**
+ * Create mock of provided class name
+ *
+ * @param string $className
+ * @return MockObject
+ */
+ public function createFixture(string $className): MockObject
+ {
+ return $this->createMock($className);
+ }
+}
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/LegacyDataFixturePathResolverTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/LegacyDataFixturePathResolverTest.php
new file mode 100644
index 0000000000000..eab34c178b907
--- /dev/null
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/LegacyDataFixturePathResolverTest.php
@@ -0,0 +1,86 @@
+model = new LegacyDataFixturePathResolver(new ComponentRegistrar());
+ }
+
+ /**
+ * Test that fixture full path is resolved correctly
+ *
+ * @param string $fixture
+ * @param string $path
+ * @dataProvider fixtureDataProvider
+ */
+ public function testResolve(string $fixture, string $path): void
+ {
+ $path = str_replace('{{basePath}}', static::$basePath, $path);
+ $this->assertEquals($path, $this->model->resolve($fixture));
+ }
+
+ /**
+ * @return array
+ */
+ public function fixtureDataProvider(): array
+ {
+ return [
+ [
+ 'Magento/Test/_files/fixture.php',
+ '{{basePath}}Magento/Test/_files/fixture.php'
+ ],
+ [
+ 'Bar_DataFixtureTest::foo/bar/baz/fixture.php',
+ '{{basePath}}Bar/DataFixtureTest/foo/bar/baz/fixture.php'
+ ]
+ ];
+ }
+}
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/LegacyDataFixtureTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/LegacyDataFixtureTest.php
new file mode 100644
index 0000000000000..b02fc6aa13101
--- /dev/null
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Fixture/LegacyDataFixtureTest.php
@@ -0,0 +1,78 @@
+createMock(LegacyDataFixturePathResolver::class);
+ $fixturePath = 'Magento/Test/Annotation/_files/sample_fixture_three.php';
+ $pathResolver->method('resolve')
+ ->willReturnCallback([$this, 'getFixtureAbsolutePath']);
+ $this->model = new LegacyDataFixture(
+ $pathResolver,
+ $fixturePath
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ putenv('sample_fixture_three');
+ }
+
+ /**
+ * Test that the fixture is executed
+ */
+ public function testApply(): void
+ {
+ $this->model->apply();
+ $this->assertEquals('3', getenv('sample_fixture_three'));
+ }
+
+ /**
+ * Test that the rollback fixture is executed
+ */
+ public function testRevert(): void
+ {
+ $this->model->apply();
+ $this->model->revert(new DataObject());
+ $this->assertEquals('', getenv('sample_fixture_three'));
+ }
+
+ /**
+ * Get the absolute path of provided fixture
+ *
+ * @param string $path
+ * @return string
+ */
+ public function getFixtureAbsolutePath(string $path): string
+ {
+ return dirname(__FILE__, 4) . DIRECTORY_SEPARATOR . $path;
+ }
+}
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Workaround/Override/Fixture/Applier/DataFixtureTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Workaround/Override/Fixture/Applier/DataFixtureTest.php
index 921c78e7bd482..5c301cafa4914 100644
--- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Workaround/Override/Fixture/Applier/DataFixtureTest.php
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Workaround/Override/Fixture/Applier/DataFixtureTest.php
@@ -80,7 +80,7 @@ public function fixturesProvider(): array
{
return [
'sort_fixtures_before_all' => [
- 'existing_fixtures' => ['fixture'],
+ 'existing_fixtures' => [['factory' => 'fixture']],
'config' => [
[
'path' => 'added_fixture',
@@ -90,10 +90,10 @@ public function fixturesProvider(): array
'remove' => false,
]
],
- 'expected_order' => ['added_fixture', 'fixture'],
+ 'expected_order' => [['factory' => 'added_fixture'], ['factory' => 'fixture']],
],
'sort_fixtures_after_all' => [
- 'existing_fixtures' => ['fixture'],
+ 'existing_fixtures' => [['factory' => 'fixture']],
'config' => [
[
'path' => 'added_fixture',
@@ -103,10 +103,10 @@ public function fixturesProvider(): array
'remove' => false,
]
],
- 'expected_order' => ['fixture', 'added_fixture'],
+ 'expected_order' => [['factory' => 'fixture'], ['factory' => 'added_fixture']],
],
'sort_fixture_before_specific' => [
- 'existing_fixtures' => ['fixture1', 'fixture2'],
+ 'existing_fixtures' => [['factory' => 'fixture1'], ['factory' => 'fixture2']],
'config' => [
[
'path' => 'added_fixture',
@@ -116,10 +116,18 @@ public function fixturesProvider(): array
'remove' => false,
]
],
- 'expected_order' => ['fixture1', 'added_fixture', 'fixture2'],
+ 'expected_order' => [
+ ['factory' => 'fixture1'],
+ ['factory' => 'added_fixture'],
+ ['factory' => 'fixture2']
+ ],
],
'sort_fixture_after_specific' => [
- 'existing_fixtures' => ['fixture1', 'fixture2', 'fixture3'],
+ 'existing_fixtures' => [
+ ['factory' => 'fixture1'],
+ ['factory' => 'fixture2'],
+ ['factory' => 'fixture3']
+ ],
'config' => [
[
'path' => 'added_fixture',
@@ -129,7 +137,12 @@ public function fixturesProvider(): array
'remove' => false,
]
],
- 'expected_order' => ['fixture1', 'fixture2', 'added_fixture', 'fixture3'],
+ 'expected_order' => [
+ ['factory' => 'fixture1'],
+ ['factory' => 'fixture2'],
+ ['factory' => 'added_fixture'],
+ ['factory' => 'fixture3']
+ ],
],
];
}
@@ -155,7 +168,7 @@ public function removeFixturesProvider(): array
{
return [
'remove_fixture' => [
- 'existing_fixtures' => ['fixture', 'fixture2'],
+ 'existing_fixtures' => [['factory' => 'fixture'], ['factory' => 'fixture2']],
'config' => [
[
'path' => 'fixture',
@@ -165,10 +178,10 @@ public function removeFixturesProvider(): array
'remove' => true,
]
],
- 'expected_order' => ['fixture2'],
+ 'expected_order' => [['factory' => 'fixture2']],
],
'remove_one_of_same_fixtures' => [
- 'existing_fixtures' => ['fixture', 'fixture', 'fixture2'],
+ 'existing_fixtures' => [['factory' => 'fixture'], ['factory' => 'fixture'], ['factory' => 'fixture2']],
'config' => [
[
'path' => 'fixture',
@@ -178,10 +191,10 @@ public function removeFixturesProvider(): array
'remove' => true,
]
],
- 'expected_order' => ['fixture', 'fixture2'],
+ 'expected_order' => [['factory' => 'fixture'], ['factory' => 'fixture2']],
],
'remove_all_of_same_fixtures' => [
- 'existing_fixtures' => ['fixture', 'fixture', 'fixture2'],
+ 'existing_fixtures' => [['factory' => 'fixture'], ['factory' => 'fixture'], ['factory' => 'fixture2']],
'config' => [
[
'path' => 'fixture',
@@ -198,7 +211,7 @@ public function removeFixturesProvider(): array
'remove' => true,
]
],
- 'expected_order' => ['fixture2'],
+ 'expected_order' => [['factory' => 'fixture2']],
],
];
}
@@ -224,7 +237,7 @@ public function replaceFixturesProvider(): array
{
return [
'replace_one_fixture' => [
- 'existing_fixtures' => ['fixture', 'fixture2'],
+ 'existing_fixtures' => [['factory' => 'fixture'], ['factory' => 'fixture2']],
'config' => [
[
'path' => 'fixture',
@@ -234,10 +247,10 @@ public function replaceFixturesProvider(): array
'remove' => false,
]
],
- 'expected_order' => ['new_fixture', 'fixture2'],
+ 'expected_order' => [['factory' => 'new_fixture'], ['factory' => 'fixture2']],
],
'replace_all_fixture' => [
- 'existing_fixtures' => ['fixture', 'fixture', 'fixture2'],
+ 'existing_fixtures' => [['factory' => 'fixture'], ['factory' => 'fixture'], ['factory' => 'fixture2']],
'config' => [
[
'path' => 'fixture',
@@ -247,7 +260,11 @@ public function replaceFixturesProvider(): array
'remove' => false,
]
],
- 'expected_order' => ['new_fixture', 'new_fixture', 'fixture2'],
+ 'expected_order' => [
+ ['factory' => 'new_fixture'],
+ ['factory' => 'new_fixture'],
+ ['factory' => 'fixture2']
+ ],
],
];
}
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Workaround/Override/Fixture/ResolverTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Workaround/Override/Fixture/ResolverTest.php
index 2531cc41f4abc..f6494d128c95f 100644
--- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Workaround/Override/Fixture/ResolverTest.php
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Workaround/Override/Fixture/ResolverTest.php
@@ -18,65 +18,6 @@
*/
class ResolverTest extends TestCase
{
- /**
- * Dummy fixture
- *
- * @return void
- */
- public static function dummyFixture(): void
- {
- }
-
- /**
- * @return void
- */
- public function testProcessFixturePath(): void
- {
- if (!defined('INTEGRATION_TESTS_DIR')) {
- define('INTEGRATION_TESTS_DIR', __DIR__);
- }
- $fixture = $this->processFixturePath('Some/Module/_files/some_fixture.php');
- $this->assertEquals(
- INTEGRATION_TESTS_DIR . '/testsuite/' . 'Some/Module/_files/some_fixture.php',
- $fixture
- );
- }
-
- /**
- * @return void
- */
- public function testProcessFixturePathCallableFixture(): void
- {
- $fixture = $this->processFixturePath('dummyFixture');
- $this->assertTrue(is_array($fixture));
- $this->assertNotFalse(array_search('dummyFixture', $fixture));
- $this->assertNotFalse(array_search(get_class($this), $fixture));
- }
-
- /**
- * @return void
- */
- public function testProcessFixturePathNotRegisteredModule(): void
- {
- $this->expectException(LocalizedException::class);
- $this->expectExceptionMessage('Can\'t find registered Module with name Some_Module .');
- $this->processFixturePath('Some_Module::some_fixture.php');
- }
-
- /**
- * @return void
- */
- public function testProcessFixturePathRegisteredModule(): void
- {
- ComponentRegistrar::register(
- ComponentRegistrar::MODULE,
- 'Some_Module',
- __DIR__
- );
- $fixture = $this->processFixturePath('Some_Module::some_fixture.php');
- $this->assertStringEndsWith('some_fixture.php', $fixture);
- }
-
/**
* @return void
*/
@@ -103,23 +44,6 @@ public function testRequireDataFixture(): void
$resolver->requireDataFixture('path/to/fixture.php');
}
- /**
- * Invoke resolver processFixturePath method
- *
- * @param string $annotation
- * @return string|array
- */
- private function processFixturePath(string $annotation)
- {
- $resolverMock = $this->createResolverMock();
- $resolverMock->method('getComponentRegistrar')->willReturn(new ComponentRegistrar());
- $reflection = new \ReflectionClass(Resolver::class);
- $reflectionMethod = $reflection->getMethod('processFixturePath');
- $reflectionMethod->setAccessible(true);
-
- return $reflectionMethod->invoke($resolverMock, $this, $annotation);
- }
-
/**
* Create mock for resolver object
*
diff --git a/dev/tests/integration/phpunit.xml.dist b/dev/tests/integration/phpunit.xml.dist
index 8941ae0ab7cb1..47fb71f3eeed0 100644
--- a/dev/tests/integration/phpunit.xml.dist
+++ b/dev/tests/integration/phpunit.xml.dist
@@ -132,6 +132,9 @@
magentoIndexerDimensionMode
+
+ magentoDataFixtureDataProvider
+
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php
index fb1afc5040c03..02c41c305d5f5 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php
@@ -143,14 +143,23 @@ public function testFixedBundleProductPriceWithCatalogRule(): void
/**
* Fixed Bundle Product without discounts
- * @magentoDataFixture Magento/Bundle/_files/fixed_bundle_product_without_discounts.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple1","price":10} as:p1
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple2","price":20} as:p2
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple3","price":30} as:p3
+ * @magentoDataFixture Magento\Bundle\Test\Fixture\Link with:{"sku":"$p1.sku$","price":10,"price_type":0} as:link1
+ * @magentoDataFixture Magento\Bundle\Test\Fixture\Link with:{"sku":"$p2.sku$","price":25,"price_type":1} as:link2
+ * @magentoDataFixture Magento\Bundle\Test\Fixture\Link with:{"sku":"$p3.sku$","price":25,"price_type":0} as:link3
+ * @magentoDataFixture Magento\Bundle\Test\Fixture\Option as:opt1
+ * @magentoDataFixture Magento\Bundle\Test\Fixture\Product as:bundle1
+ * @magentoDataFixtureDataProvider {"opt1":{"product_links":["$link1$","$link2$","$link3$"]}}
+ * @magentoDataFixtureDataProvider {"bundle1":{"sku":"bundle1","price":50,"price_type":1,"_options":["$opt1$"]}}
*
* @return void
*/
public function testFixedBundleProductPriceWithoutDiscounts(): void
{
$this->checkBundlePrices(
- 'fixed_bundle_product_without_discounts',
+ 'bundle1',
['price' => 50, 'final_price' => 50, 'min_price' => 60, 'max_price' => 75, 'tier_price' => null],
['simple1' => 60, 'simple2' => 62.5, 'simple3' => 75]
);
@@ -188,14 +197,19 @@ public function testFixedBundleProductPriceWithTierPrice(): void
/**
* Dynamic Bundle Product without discount + options without discounts
- * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple1000","price":10} as:p1
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple1001","price":20} as:p2
+ * @magentoDataFixture Magento\Bundle\Test\Fixture\Option as:opt1
+ * @magentoDataFixture Magento\Bundle\Test\Fixture\Product as:bundle1
+ * @magentoDataFixtureDataProvider {"opt1":{"product_links":["$p1$","$p2$"]}}
+ * @magentoDataFixtureDataProvider {"bundle1":{"sku":"bundle1","_options":["$opt1$"]}}
*
* @return void
*/
public function testDynamicBundleProductWithoutDiscountAndOptionsWithoutDiscounts(): void
{
$this->checkBundlePrices(
- 'dynamic_bundle_product_without_discounts',
+ 'bundle1',
['price' => 0, 'final_price' => 0, 'min_price' => 10, 'max_price' => 20, 'tier_price' => null],
['simple1000' => 10, 'simple1001' => 20]
);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/PriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/PriceTest.php
index 19a23d8543870..f814d60c48179 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/PriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/PriceTest.php
@@ -6,8 +6,10 @@
namespace Magento\Catalog\Model\Product\Attribute\Backend;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Model\ResourceModel\Product;
use Magento\Catalog\Observer\SwitchPriceAttributeScopeOnConfigChange;
use Magento\Framework\App\Config\ReinitableConfigInterface;
+use Magento\TestFramework\Fixture\DataFixtureStorageManager;
/**
* Test class for \Magento\Catalog\Model\Product\Attribute\Backend\Price.
@@ -30,6 +32,16 @@ class PriceTest extends \PHPUnit\Framework\TestCase
/** @var ProductRepositoryInterface */
private $productRepository;
+ /**
+ * @var Product
+ */
+ private $productResource;
+
+ /**
+ * @var \Magento\TestFramework\Fixture\DataFixtureStorage
+ */
+ private $fixtures;
+
protected function setUp(): void
{
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
@@ -49,6 +61,10 @@ protected function setUp(): void
$this->productRepository = $this->objectManager->create(
ProductRepositoryInterface::class
);
+ $this->productResource = $this->objectManager->create(
+ Product::class
+ );
+ $this->fixtures = $this->objectManager->get(DataFixtureStorageManager::class)->getStorage();
$this->model->setAttribute(
$this->objectManager->get(
\Magento\Eav\Model\Config::class
@@ -103,14 +119,17 @@ public function testAfterSave()
$product = $this->productRepository->get('simple');
$product->setPrice('9.99');
$product->setStoreId($globalStoreId);
- $product->getResource()->save($product);
+ $this->productResource->save($product);
$product = $this->productRepository->get('simple', false, $globalStoreId, true);
$this->assertEquals('9.990000', $product->getPrice());
}
/**
- * @magentoDataFixture Magento/Catalog/_files/product_simple.php
- * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ * @magentoDataFixture Magento\Store\Test\Fixture\Website as:website2
+ * @magentoDataFixture Magento\Store\Test\Fixture\Group with:{"website_id":"$website2.id$"} as:store_group2
+ * @magentoDataFixture Magento\Store\Test\Fixture\Store with:{"store_group_id":"$store_group2.id$"} as:store2
+ * @magentoDataFixture Magento\Store\Test\Fixture\Store with:{"store_group_id":"$store_group2.id$"} as:store3
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product as:product
* @magentoConfigFixture current_store catalog/price/scope 1
* @magentoDbIsolation disabled
* @magentoAppArea adminhtml
@@ -122,27 +141,28 @@ public function testAfterSaveWithDifferentStores()
\Magento\Store\Model\Store::class
);
$globalStoreId = $store->load('admin')->getId();
- $secondStoreId = $store->load('fixture_second_store')->getId();
- $thirdStoreId = $store->load('fixture_third_store')->getId();
+ $secondStoreId = $this->fixtures->get('store2')->getId();
+ $thirdStoreId = $this->fixtures->get('store3')->getId();
+ $productSku = $this->fixtures->get('product')->getSku();
/** @var \Magento\Catalog\Model\Product\Action $productAction */
$productAction = $this->objectManager->create(
\Magento\Catalog\Model\Product\Action::class
);
- $product = $this->productRepository->get('simple');
+ $product = $this->productRepository->get($productSku);
$productId = $product->getId();
$productAction->updateWebsites([$productId], [$store->load('fixture_second_store')->getWebsiteId()], 'add');
$product->setStoreId($secondStoreId);
$product->setPrice('9.99');
- $product->getResource()->save($product);
+ $this->productResource->save($product);
- $product = $this->productRepository->get('simple', false, $globalStoreId, true);
+ $product = $this->productRepository->get($productSku, false, $globalStoreId, true);
$this->assertEquals(10, $product->getPrice());
- $product = $this->productRepository->get('simple', false, $secondStoreId, true);
+ $product = $this->productRepository->get($productSku, false, $secondStoreId, true);
$this->assertEquals('9.990000', $product->getPrice());
- $product = $this->productRepository->get('simple', false, $thirdStoreId, true);
+ $product = $this->productRepository->get($productSku, false, $thirdStoreId, true);
$this->assertEquals('9.990000', $product->getPrice());
}
@@ -173,7 +193,7 @@ public function testAfterSaveWithSameCurrency()
$product->setOrigData();
$product->setStoreId($secondStoreId);
$product->setPrice('9.99');
- $product->getResource()->save($product);
+ $this->productResource->save($product);
$product = $this->productRepository->get('simple', false, $globalStoreId, true);
$this->assertEquals(10, $product->getPrice());
@@ -212,7 +232,7 @@ public function testAfterSaveWithUseDefault()
$product->setOrigData();
$product->setStoreId($secondStoreId);
$product->setPrice('9.99');
- $product->getResource()->save($product);
+ $this->productResource->save($product);
$product = $this->productRepository->get('simple', false, $globalStoreId, true);
$this->assertEquals(10, $product->getPrice());
@@ -225,7 +245,7 @@ public function testAfterSaveWithUseDefault()
$product->setStoreId($thirdStoreId);
$product->setPrice(null);
- $product->getResource()->save($product);
+ $this->productResource->save($product);
$product = $this->productRepository->get('simple', false, $globalStoreId, true);
$this->assertEquals(10, $product->getPrice());
@@ -281,7 +301,7 @@ public function testAfterSaveForWebsitesWithDifferentCurrencies()
$product->setOrigData();
$product->setStoreId($globalStoreId);
$product->setPrice(100);
- $product->getResource()->save($product);
+ $this->productResource->save($product);
$product = $this->productRepository->get('simple', false, $globalStoreId, true);
$this->assertEquals(100, $product->getPrice());
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
index 885a7dff06633..9f73d09776318 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php
@@ -11,6 +11,7 @@
use Magento\Framework\App\Area;
use Magento\Framework\App\State;
use Magento\Store\Model\Store;
+use Magento\TestFramework\Fixture\DataFixtureStorageManager;
use Magento\TestFramework\Helper\Bootstrap;
/**
@@ -35,6 +36,11 @@ class CollectionTest extends \PHPUnit\Framework\TestCase
*/
private $productRepository;
+ /**
+ * @var \Magento\TestFramework\Fixture\DataFixtureStorage
+ */
+ private $fixtures;
+
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
@@ -52,6 +58,7 @@ protected function setUp(): void
$this->productRepository = Bootstrap::getObjectManager()->create(
\Magento\Catalog\Api\ProductRepositoryInterface::class
);
+ $this->fixtures = Bootstrap::getObjectManager()->get(DataFixtureStorageManager::class)->getStorage();
}
/**
@@ -144,19 +151,29 @@ public function testSetCategoryWithStoreFilter()
}
/**
- * @magentoDataFixture Magento/Catalog/_files/categories.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Category as:c1
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Category with:{"parent_id":"$c1.id$","is_anchor":0} as:c11
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Category with:{"parent_id":"$c1.id$","is_anchor":0} as:c12
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Category with:{"parent_id":"$c11.id$"} as:c111
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Category as:c2
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"category_ids":["$c1.id$"]} as:p1
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"category_ids":["$c111.id$"]} as:p2
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"category_ids":["$c12.id$"]} as:p3
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"category_ids":["$c2.id$"]} as:p4
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"category_ids":["$c2.id$"]} as:p5
* @magentoAppIsolation enabled
* @magentoDbIsolation disabled
*/
public function testSetCategoryFilter()
{
+ $categoryId = $this->fixtures->get('c1')->getId();
$appState = Bootstrap::getObjectManager()
->create(State::class);
$appState->setAreaCode(Area::AREA_CRONTAB);
$category = \Magento\Framework\App\ObjectManager::getInstance()->get(
\Magento\Catalog\Model\Category::class
- )->load(3);
+ )->load($categoryId);
$this->collection->addCategoryFilter($category);
$this->collection->load();
$this->assertEquals($this->collection->getSize(), 3);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php
index ab810aec2c835..85c350166c65f 100755
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php
@@ -6,10 +6,6 @@
namespace Magento\Catalog\Model\ResourceModel;
use Magento\Catalog\Api\ProductRepositoryInterface;
-use Magento\Catalog\Model\ResourceModel\Product\Action;
-use Magento\Eav\Api\Data\AttributeSetInterface;
-use Magento\Eav\Model\AttributeSetRepository;
-use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\ObjectManagerInterface;
use Magento\TestFramework\Eav\Model\GetAttributeSetByName;
use Magento\TestFramework\Helper\Bootstrap;
@@ -56,7 +52,7 @@ protected function setUp(): void
/**
* Checks a possibility to retrieve product raw attribute value.
*
- * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku": "simple"}
*/
public function testGetAttributeRawValue()
{
@@ -70,7 +66,8 @@ public function testGetAttributeRawValue()
/**
* @magentoAppArea adminhtml
- * @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_store_scope_attribute.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Attribute with:{"attribute_code": "prod_attr"}
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku": "simple"}
* @throws NoSuchEntityException
* @throws CouldNotSaveException
* @throws InputException
@@ -78,17 +75,18 @@ public function testGetAttributeRawValue()
*/
public function testGetAttributeRawValueGetDefault()
{
- $product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 0, true);
- $product->setCustomAttribute('store_scoped_attribute_code', 'default_value');
+ $product = $this->productRepository->get('simple', true, 0, true);
+ $product->setCustomAttribute('prod_attr', 'default_value');
$this->productRepository->save($product);
- $actual = $this->model->getAttributeRawValue($product->getId(), 'store_scoped_attribute_code', 1);
+ $actual = $this->model->getAttributeRawValue($product->getId(), 'prod_attr', 1);
$this->assertEquals('default_value', $actual);
}
/**
* @magentoAppArea adminhtml
- * @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_store_scope_attribute.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Attribute with:{"attribute_code": "prod_attr"}
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku": "simple"}
* @throws NoSuchEntityException
* @throws CouldNotSaveException
* @throws InputException
@@ -96,21 +94,22 @@ public function testGetAttributeRawValueGetDefault()
*/
public function testGetAttributeRawValueGetStoreSpecificValueNoDefault()
{
- $product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 0, true);
- $product->setCustomAttribute('store_scoped_attribute_code', null);
+ $product = $this->productRepository->get('simple', true, 0, true);
+ $product->setCustomAttribute('prod_attr', null);
$this->productRepository->save($product);
- $product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 1, true);
- $product->setCustomAttribute('store_scoped_attribute_code', 'store_value');
+ $product = $this->productRepository->get('simple', true, 1, true);
+ $product->setCustomAttribute('prod_attr', 'store_value');
$this->productRepository->save($product);
- $actual = $this->model->getAttributeRawValue($product->getId(), 'store_scoped_attribute_code', 1);
+ $actual = $this->model->getAttributeRawValue($product->getId(), 'prod_attr', 1);
$this->assertEquals('store_value', $actual);
}
/**
* @magentoAppArea adminhtml
- * @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_store_scope_attribute.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Attribute with:{"attribute_code": "prod_attr"}
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku": "simple"}
* @throws NoSuchEntityException
* @throws CouldNotSaveException
* @throws InputException
@@ -118,21 +117,22 @@ public function testGetAttributeRawValueGetStoreSpecificValueNoDefault()
*/
public function testGetAttributeRawValueGetStoreSpecificValueWithDefault()
{
- $product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 0, true);
- $product->setCustomAttribute('store_scoped_attribute_code', 'default_value');
+ $product = $this->productRepository->get('simple', true, 0, true);
+ $product->setCustomAttribute('prod_attr', 'default_value');
$this->productRepository->save($product);
- $product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 1, true);
- $product->setCustomAttribute('store_scoped_attribute_code', 'store_value');
+ $product = $this->productRepository->get('simple', true, 1, true);
+ $product->setCustomAttribute('prod_attr', 'store_value');
$this->productRepository->save($product);
- $actual = $this->model->getAttributeRawValue($product->getId(), 'store_scoped_attribute_code', 1);
+ $actual = $this->model->getAttributeRawValue($product->getId(), 'prod_attr', 1);
$this->assertEquals('store_value', $actual);
}
/**
* @magentoAppArea adminhtml
- * @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_store_scope_attribute.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Attribute with:{"attribute_code": "prod_attr"}
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku": "simple"}
* @throws NoSuchEntityException
* @throws CouldNotSaveException
* @throws InputException
@@ -141,17 +141,17 @@ public function testGetAttributeRawValueGetStoreSpecificValueWithDefault()
*/
public function testGetAttributeRawValueGetStoreValueFallbackToDefault()
{
- $product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 0, true);
- $product->setCustomAttribute('store_scoped_attribute_code', 'default_value');
+ $product = $this->productRepository->get('simple', true, 0, true);
+ $product->setCustomAttribute('prod_attr', 'default_value');
$this->productRepository->save($product);
- $actual = $this->model->getAttributeRawValue($product->getId(), 'store_scoped_attribute_code', 1);
+ $actual = $this->model->getAttributeRawValue($product->getId(), 'prod_attr', 1);
$this->assertEquals('default_value', $actual);
}
/**
* @magentoAppArea adminhtml
- * @magentoDataFixture Magento/Catalog/_files/product_special_price.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple","special_price":"5.99"}
* @magentoAppIsolation enabled
* @magentoConfigFixture default_store catalog/price/scope 1
*/
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/RelationTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/RelationTest.php
index 20d366d05ac4a..be0631a181272 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/RelationTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ResourceModel/Product/RelationTest.php
@@ -53,17 +53,25 @@ protected function setUp(): void
/**
* Tests that getRelationsByChildren will return parent products entity ids of child products entity ids.
*
- * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple_10"} as:p1
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple_20"} as:p2
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple_30"} as:p3
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple_40"} as:p4
+ * @magentoDataFixture Magento\ConfigurableProduct\Test\Fixture\Attribute as:attr
+ * @magentoDataFixture Magento\ConfigurableProduct\Test\Fixture\Product as:conf1
+ * @magentoDataFixture Magento\ConfigurableProduct\Test\Fixture\Product as:conf2
+ * @magentoDataFixtureDataProvider {"conf1":{"sku":"conf1","_options":["$attr$"],"_links":["$p1$","$p2$"]}}
+ * @magentoDataFixtureDataProvider {"conf2":{"sku":"conf2","_options":["$attr$"],"_links":["$p3$","$p4$"]}}
*/
public function testGetRelationsByChildren(): void
{
$childSkusOfParentSkus = [
- 'configurable' => ['simple_10', 'simple_20'],
- 'configurable_12345' => ['simple_30', 'simple_40'],
+ 'conf1' => ['simple_10', 'simple_20'],
+ 'conf2' => ['simple_30', 'simple_40'],
];
$configurableSkus = [
- 'configurable',
- 'configurable_12345',
+ 'conf1',
+ 'conf2',
'simple_10',
'simple_20',
'simple_30',
@@ -114,7 +122,7 @@ public function testGetRelationsByChildren(): void
*/
private function sortParentIdsOfChildIds(array $array): array
{
- foreach ($array as $childId => &$parentIds) {
+ foreach ($array as &$parentIds) {
sort($parentIds, SORT_NUMERIC);
}
diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php
index 25ddef8d7590d..6f4d58b8a5620 100644
--- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php
+++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php
@@ -49,7 +49,12 @@ public function testFactory()
}
/**
- * @magentoDataFixture Magento/GroupedProduct/_files/product_grouped.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product as:p1
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Virtual as:p2
+ * @magentoDataFixture Magento\GroupedProduct\Test\Fixture\Product as:gr1
+ * @magentoDataFixtureDataProvider {"p1":{"sku":"simple","name":"Simple Product","price":10}}
+ * @magentoDataFixtureDataProvider {"p2":{"sku":"virtual-product","name":"Virtual Product","price":10}}
+ * @magentoDataFixtureDataProvider {"gr1":{"sku":"gr1","product_links":["$p1$",{"sku":"$p2.sku$","qty":2}]}}
* @magentoAppArea frontend
*/
public function testGetAssociatedProducts()
@@ -57,7 +62,7 @@ public function testGetAssociatedProducts()
$productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
/** @var Product $product */
- $product = $productRepository->get('grouped-product');
+ $product = $productRepository->get('gr1');
$type = $product->getTypeInstance();
$this->assertInstanceOf(Grouped::class, $type);
@@ -74,14 +79,14 @@ public function testGetAssociatedProducts()
private function assertProductInfo($product)
{
$data = [
- 1 => [
+ 'simple' => [
'sku' => 'simple',
'name' => 'Simple Product',
'price' => '10.000000',
'qty' => '1',
'position' => '1'
],
- 21 => [
+ 'virtual-product' => [
'sku' => 'virtual-product',
'name' => 'Virtual Product',
'price' => '10.000000',
@@ -89,7 +94,7 @@ private function assertProductInfo($product)
'position' => '2'
]
];
- $productId = $product->getId();
+ $productId = $product->getSku();
$this->assertEquals($data[$productId]['sku'], $product->getSku());
$this->assertEquals($data[$productId]['name'], $product->getName());
$this->assertEquals($data[$productId]['price'], $product->getPrice());
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php
index 788dfb15894e0..3bf4f459b63fc 100644
--- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/CartTest.php
@@ -15,6 +15,7 @@
use Magento\Framework\Message\MessageInterface;
use Magento\TestFramework\TestCase\AbstractController;
use Magento\TestFramework\Wishlist\Model\GetWishlistByCustomerId;
+use Magento\TestFramework\Fixture\DataFixtureStorageManager;
/**
* Test for add product to cart from wish list.
@@ -39,6 +40,11 @@ class CartTest extends AbstractController
/** @var ProductRepositoryInterface */
private $productRepository;
+ /**
+ * @var \Magento\TestFramework\Fixture\DataFixtureStorage
+ */
+ private $fixtures;
+
/**
* @inheritdoc
*/
@@ -51,6 +57,7 @@ protected function setUp(): void
$this->cartFactory = $this->_objectManager->get(CartFactory::class);
$this->escaper = $this->_objectManager->get(Escaper::class);
$this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class);
+ $this->fixtures = $this->_objectManager->get(DataFixtureStorageManager::class)->getStorage();
}
/**
@@ -117,12 +124,13 @@ public function testAddNotExistingItemToCart(): void
*
* @return void
* @magentoDataFixture Magento/Wishlist/_files/wishlist_with_simple_product.php
- * @magentoDataFixture Magento/Catalog/_files/products.php
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product as:product1
+ * @magentoDataFixture Magento\Catalog\Test\Fixture\Product as:product2
*/
public function testAddItemWithRelatedProducts(): void
{
- $firstProductId = $this->productRepository->get('simple')->getId();
- $secondProductID = $this->productRepository->get('custom-design-simple-product')->getId();
+ $firstProductId = $this->fixtures->get('product1')->getId();
+ $secondProductID = $this->fixtures->get('product2')->getId();
$relatedIds = $expectedAddedIds = [$firstProductId, $secondProductID];
$this->customerSession->setCustomerId(1);