Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
AbstractProductExportImportTestCase.php
Go to the documentation of this file.
1 <?php
7 
11 
17 abstract class AbstractProductExportImportTestCase extends \PHPUnit\Framework\TestCase
18 {
22  protected $objectManager;
23 
27  protected $fileSystem;
28 
32  protected $productResource;
33 
37  protected $fixtures;
38 
44  public static $skippedAttributes = [
45  'options',
46  'created_at',
47  'updated_at',
48  'category_ids',
49  'special_from_date',
50  'news_from_date',
51  'custom_design_from',
52  'updated_in',
53  'tax_class_id',
54  'description',
55  'is_salable', // stock indexation is not performed during import
56  ];
57 
58  protected function setUp()
59  {
61  $this->fileSystem = $this->objectManager->get(\Magento\Framework\Filesystem::class);
62  $this->productResource = $this->objectManager->create(
63  \Magento\Catalog\Model\ResourceModel\Product::class
64  );
65  \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType::$commonAttributesCache = [];
66  }
67 
68  protected function tearDown()
69  {
70  $this->executeRollbackFixtures($this->fixtures);
71  }
72 
83  public function testExport($fixtures, $skus, $skippedAttributes = [])
84  {
85  $this->fixtures = $fixtures;
87  $this->modifyData($skus);
88  $skippedAttributes = array_merge(self::$skippedAttributes, $skippedAttributes);
89  $this->executeExportTest($skus, $skippedAttributes);
90  }
91 
92  abstract public function exportImportDataProvider();
93 
98  protected function modifyData($skus)
99  {
100  }
101 
106  public function prepareProduct($product)
107  {
108  }
109 
110  protected function executeExportTest($skus, $skippedAttributes)
111  {
112  $index = 0;
113  $ids = [];
114  $origProducts = [];
116  $stockRegistryStorage = $this->objectManager->get(\Magento\CatalogInventory\Model\StockRegistryStorage::class);
118  $productRepository = $this->objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
119  while (isset($skus[$index])) {
120  $ids[$index] = $this->productResource->getIdBySku($skus[$index]);
121  $origProducts[$index] = $productRepository->get($skus[$index], false, Store::DEFAULT_STORE_ID);
122  $index++;
123  }
124 
125  $csvfile = $this->exportProducts();
126  $this->importProducts($csvfile, \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND);
127 
128  while ($index > 0) {
129  $index--;
130  $stockRegistryStorage->removeStockItem($ids[$index]);
131  $newProduct = $productRepository->get($skus[$index], false, Store::DEFAULT_STORE_ID, true);
132  // @todo uncomment or remove after MAGETWO-49806 resolved
133  //$this->assertEquals(count($origProductData[$index]), count($newProductData));
134 
135  $this->assertEqualsOtherThanSkippedAttributes(
136  $origProducts[$index]->getData(),
137  $newProduct->getData(),
139  );
140 
141  $this->assertEqualsSpecificAttributes($origProducts[$index], $newProduct);
142  }
143  }
144 
145  private function assertEqualsOtherThanSkippedAttributes($expected, $actual, $skippedAttributes)
146  {
147  foreach ($expected as $key => $value) {
148  if (is_object($value) || in_array($key, $skippedAttributes)) {
149  continue;
150  }
151 
152  $this->assertEquals(
153  $value,
154  isset($actual[$key]) ? $actual[$key] : null,
155  'Assert value at key - ' . $key . ' failed'
156  );
157  }
158  }
159 
171  {
172  $this->fixtures = $fixtures;
174  $this->modifyData($skus);
175  $this->executeImportDeleteTest($skus);
176  }
177 
178  protected function executeImportDeleteTest($skus)
179  {
180  $csvfile = $this->exportProducts();
181  $this->importProducts($csvfile, \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE);
183  $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class);
184  foreach ($skus as $sku) {
185  $productId = $this->productResource->getIdBySku($sku);
186  $product->load($productId);
187  $this->assertNull($product->getId());
188  }
189  }
190 
199  protected function executeFixtures($fixtures, $skus = [])
200  {
201  foreach ($fixtures as $fixture) {
202  $fixturePath = $this->fileSystem->getDirectoryRead(DirectoryList::ROOT)
203  ->getAbsolutePath('/dev/tests/integration/testsuite/' . $fixture);
204  include $fixturePath;
205  }
206  }
207 
214  private function executeRollbackFixtures($fixtures)
215  {
216  foreach ($fixtures as $fixture) {
217  $fixturePath = $this->fileSystem->getDirectoryRead(DirectoryList::ROOT)
218  ->getAbsolutePath('/dev/tests/integration/testsuite/' . $fixture);
219  $fileInfo = pathinfo($fixturePath);
220  $extension = '';
221  if (isset($fileInfo['extension'])) {
222  $extension = '.' . $fileInfo['extension'];
223  }
224  $rollbackfixturePath = $fileInfo['dirname'] . '/' . $fileInfo['filename'] . '_rollback' . $extension;
225  if (file_exists($rollbackfixturePath)) {
226  include $rollbackfixturePath;
227  }
228  }
229  }
230 
236  protected function assertEqualsSpecificAttributes($expectedProduct, $actualProduct)
237  {
238  // check custom options
239  }
240 
252  {
253  $this->fixtures = $fixtures;
255  $this->modifyData($skus);
256  $skippedAttributes = array_merge(self::$skippedAttributes, $skippedAttributes);
257  $this->executeImportReplaceTest($skus, $skippedAttributes);
258  }
259 
271  {
272  $this->fixtures = $fixtures;
274  $this->modifyData($skus);
275  $skippedAttributes = array_merge(self::$skippedAttributes, $skippedAttributes);
276  $this->executeImportReplaceTest($skus, $skippedAttributes, true);
277  }
278 
287  protected function executeImportReplaceTest($skus, $skippedAttributes, $usePagination = false)
288  {
289  $replacedAttributes = [
290  'row_id',
291  'entity_id',
292  'tier_price',
293  'media_gallery'
294  ];
295  $skippedAttributes = array_merge($replacedAttributes, $skippedAttributes);
296 
297  $index = 0;
298  $ids = [];
299  $origProducts = [];
301  $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
302  while (isset($skus[$index])) {
303  $ids[$index] = $this->productResource->getIdBySku($skus[$index]);
304  $origProducts[$index] = $productRepository->get($skus[$index], false, Store::DEFAULT_STORE_ID);
305  $index++;
306  }
307 
308  $exportProduct = $this->objectManager->create(\Magento\CatalogImportExport\Model\Export\Product::class);
309  if ($usePagination) {
311  $itemsPerPageProperty = $this->objectManager->create(\ReflectionProperty::class, [
312  'class' => \Magento\CatalogImportExport\Model\Export\Product::class,
313  'name' => '_itemsPerPage'
314  ]);
315  $itemsPerPageProperty->setAccessible(true);
316  $itemsPerPageProperty->setValue($exportProduct, 1);
317  }
318 
319  $csvfile = $this->exportProducts($exportProduct);
320  $this->importProducts($csvfile, \Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE);
321 
322  while ($index > 0) {
323  $index--;
324  $newProduct = $productRepository->get($skus[$index], false, Store::DEFAULT_STORE_ID, true);
325  // check original product is deleted
326  $origProduct = $this->objectManager->create(\Magento\Catalog\Model\Product::class)->load($ids[$index]);
327  $this->assertNull($origProduct->getId());
328 
329  // check new product data
330  // @todo uncomment or remove after MAGETWO-49806 resolved
331  //$this->assertEquals(count($origProductData[$index]), count($newProductData));
332 
333  $origProductData = $origProducts[$index]->getData();
334  $newProductData = $newProduct->getData();
335  $this->assertEqualsOtherThanSkippedAttributes($origProductData, $newProductData, $skippedAttributes);
336 
337  $this->assertEqualsSpecificAttributes($origProducts[$index], $newProduct);
338 
339  foreach ($replacedAttributes as $attribute) {
340  if (isset($origProductData[$attribute])) {
341  $expected = is_array($origProductData[$attribute]) ?
342  array_filter($origProductData[$attribute]) :
343  $origProductData[$attribute];
344  if (!empty($expected)) {
345  $actual = isset($newProductData[$attribute]) ? $newProductData[$attribute] : null;
346  $actual = is_array($actual) ? array_filter($actual) : $actual;
347  $this->assertNotEquals($expected, $actual, $attribute . ' is expected to be changed');
348  }
349  }
350  }
351  }
352  }
353 
360  private function exportProducts(\Magento\CatalogImportExport\Model\Export\Product $exportProduct = null)
361  {
362  $csvfile = uniqid('importexport_') . '.csv';
363 
364  $exportProduct = $exportProduct ?: $this->objectManager->create(
365  \Magento\CatalogImportExport\Model\Export\Product::class
366  );
367  $exportProduct->setWriter(
368  \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
369  \Magento\ImportExport\Model\Export\Adapter\Csv::class,
370  ['fileSystem' => $this->fileSystem, 'destination' => $csvfile]
371  )
372  );
373  $this->assertNotEmpty($exportProduct->export());
374  return $csvfile;
375  }
376 
384  private function importProducts($csvfile, $behavior)
385  {
387  $importModel = $this->objectManager->create(
388  \Magento\CatalogImportExport\Model\Import\Product::class
389  );
390  $directory = $this->fileSystem->getDirectoryWrite(DirectoryList::VAR_DIR);
391  $source = $this->objectManager->create(
392  \Magento\ImportExport\Model\Import\Source\Csv::class,
393  [
394  'file' => $csvfile,
395  'directory' => $directory
396  ]
397  );
398 
399  $appParams = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap()
400  ->getApplication()
402  $uploader = $importModel->getUploader();
403  $rootDirectory = $this->fileSystem->getDirectoryWrite(DirectoryList::ROOT);
404  $destDir = $rootDirectory->getRelativePath(
405  $appParams[DirectoryList::MEDIA][DirectoryList::PATH] . '/catalog/product'
406  );
407  $tmpDir = $rootDirectory->getRelativePath(
408  $appParams[DirectoryList::MEDIA][DirectoryList::PATH] . '/import'
409  );
410 
411  $rootDirectory->create($destDir);
412  $rootDirectory->create($tmpDir);
413  $this->assertTrue($uploader->setDestDir($destDir));
414  $this->assertTrue($uploader->setTmpDir($tmpDir));
415 
416  $errors = $importModel->setParameters(
417  [
418  'behavior' => $behavior,
419  'entity' => 'catalog_product',
420  ]
421  )->setSource(
422  $source
423  )->validateData();
424  $errorMessage = $this->extractErrorMessage($errors->getAllErrors());
425 
426  $this->assertEmpty(
427  $errorMessage,
428  'Product import from file ' . $csvfile . ' validation errors: ' . $errorMessage
429  );
430  $importModel->importData();
431  $importErrors = $importModel->getErrorAggregator()->getAllErrors();
432  $importErrorMessage = $this->extractErrorMessage($importErrors);
433  $this->assertEmpty(
434  $importErrorMessage,
435  'Product import from file ' . $csvfile . ' errors: ' . $importErrorMessage
436  );
437  }
438 
443  private function extractErrorMessage($errors)
444  {
445  $errorMessage = '';
446  foreach ($errors as $error) {
447  $errorMessage = "\n" . $error->getErrorMessage();
448  }
449  return $errorMessage;
450  }
451 }
$source
Definition: source.php:23
foreach($websiteCodes as $websiteCode) $skus
$stockRegistryStorage
$value
Definition: gender.phtml:16
$rootDirectory
$index
Definition: list.phtml:44
$errors
Definition: overview.phtml:9