Integration Test Database Setup
Test Database Configuration
// dev/tests/integration/etc/install-config.php
return [
'db-host' => 'localhost',
'db-user' => 'root',
'db-password' => '',
'db-name' => 'magento_integration_tests',
'db-engine' => 'mysql',
'backend-frontname' => 'backend',
'admin-email' => 'admin@example.com',
'admin-user' => 'admin',
'admin-password' => 'admin123',
'language' => 'en_US',
'currency' => 'USD',
'timezone' => 'America/Chicago',
];
PHPUnit Integration Config
<!-- dev/tests/integration/phpunit.xml.dist -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="../bootstrap.php"
colors="true">
<testsuites>
<testsuite name="Magento Integration Tests">
<directory>../../../app/code/Vendor/Module/Test/Integration</directory>
</testsuite>
</testsuites>
<php>
<env name="MAGE_MODE" value="developer" />
<env name="APP_ENV" value="integration_test" />
</php>
</phpunit>
Running Integration Tests
# Setup test database
bin/magento setup:install --db-name=magento_integration_tests
# Run integration tests
vendor/bin/phpunit -c dev/tests/integration/phpunit.xml.dist
# Run with coverage
vendor/bin/phpunit --coverage-html dev/tests/integration/coverage \
-c dev/tests/integration/phpunit.xml.dist
Key Points
- Integration tests use a real database
- Tests should be isolated and not affect each other
- Use transactions for test isolation
- Clean up test data after each test
Test Fixtures
File-Based Fixtures
// dev/tests/integration/fixtures/product.json
[
{
"sku": "TEST-PRODUCT-001",
"name": "Test Product",
"price": 99.99,
"status": 1,
"visibility": 4,
"type_id": "simple",
"attribute_set_id": 4
}
]
PHP Fixtures
// dev/tests/integration/fixtures/category.php
<?php
return [
'category' => [
'name' => 'Test Category',
'is_active' => 1,
'path' => '1/2/3',
'level' => 2,
'position' => 1,
],
];
Fixture Loading in Tests
namespace Vendor\Module\Test\Integration\Model;
use PHPUnit\Framework\TestCase;
use Magento\TestFramework\Helper\Bootstrap;
class ProductRepositoryTest extends TestCase
{
/**
* @magentoDbIsolation enabled
* @magentoAppIsolation enabled
* @magentoDataFixture product.json
*/
public function testFindProduct()
{
$repository = Bootstrap::getObjectManager()
->create(\Magento\Catalog\Model\ProductRepository::class);
$product = $repository->get('TEST-PRODUCT-001');
$this->assertEquals('Test Product', $product->getName());
$this->assertEquals(99.99, $product->getPrice());
}
}
Key Points
- Fixtures provide consistent test data
- Use annotations to load fixtures
- Fixtures can be JSON or PHP files
- Keep fixtures minimal and focused
Setup and Teardown
Test Lifecycle
namespace Vendor\Module\Test\Integration\Model;
use PHPUnit\Framework\TestCase;
use Magento\TestFramework\Helper\Bootstrap;
class OrderTest extends TestCase
{
protected function setUp(): void
{
// Initialize test environment
$this->objectManager = Bootstrap::getObjectManager();
$this->orderFactory = $this->objectManager->get(
\Magento\Sales\Model\OrderFactory::class
);
}
protected function tearDown(): void
{
// Clean up test data
$this->objectManager->get(
\Magento\Framework\App\ResourceConnection::class
)->getConnection()->rollBack();
}
public function testCreateOrder()
{
$order = $this->orderFactory->create();
$order->setData([
'increment_id' => '100000001',
'customer_id' => 1,
'status' => 'pending',
]);
$order->save();
$this->assertNotEmpty($order->getId());
}
}
Global Setup/Teardown
// dev/tests/integration/setup.sh
#!/bin/bash
# Create test database
mysql -u root -e "CREATE DATABASE IF NOT EXISTS magento_integration_tests;"
# Install Magento
bin/magento setup:install \
--db-name=magento_integration_tests \
--admin-user=admin \
--admin-password=admin123 \
--backend-frontname=backend
# Create admin user
bin/magento admin:user:create \
--admin-user=admin \
--admin-password=admin123 \
--admin-email=admin@example.com
Key Points
- setUp() runs before each test method
- tearDown() runs after each test method
- Use transactions for quick cleanup
- Global setup runs once for entire test suite
Test Isolation
Database Isolation
namespace Vendor\Module\Test\Integration\Model;
use PHPUnit\Framework\TestCase;
use Magento\TestFramework\Helper\Bootstrap;
class CustomerTest extends TestCase
{
/**
* @magentoDbIsolation enabled
*/
public function testCreateCustomer()
{
$customer = Bootstrap::getObjectManager()->create(
\Magento\Customer\Model\Customer::class
);
$customer->setData([
'email' => 'test@example.com',
'firstname' => 'Test',
'lastname' => 'Customer',
]);
$customer->save();
// This change is rolled back after test
$this->assertNotEmpty($customer->getId());
}
}
Application Isolation
/**
* @magentoAppIsolation enabled
*/
public function testWithDifferentConfig()
{
// This test gets a fresh application instance
$config = Bootstrap::getObjectManager()->get(
\Magento\Framework\App\Config\ReinitableConfigInterface::class
);
$config->reinit();
// Modify configuration
$this->objectManager->get(
\Magento\Framework\App\Config\ValueInterface::class
)->setValue('test/value', 'new_value')->save();
// Test with new config
}
Key Points
- @magentoDbIsolation wraps tests in transactions
- @magentoAppIsolation provides fresh application instance
- Use isolation for tests that modify state
- Consider performance impact of isolation
Practice Problems
0 / 1 solved
Integration Test Suite
Create an integration test that verifies product creation and retrieval.
Solution
<?php
namespace Vendor\Module\Test\Integration\Model;
use PHPUnit\Framework\TestCase;
use Magento\TestFramework\Helper\Bootstrap;
class ProductIntegrationTest extends TestCase
{
/**
* @magentoDbIsolation enabled
*/
public function testCreateAndRetrieveProduct()
{
$objectManager = Bootstrap::getObjectManager();
$productFactory = $objectManager->get(
\Magento\Catalog\Model\ProductFactory::class
);
// Create product
$product = $productFactory->create();
$product->setData([
'sku' => 'INT-TEST-001',
'name' => 'Integration Test Product',
'price' => 49.99,
'status' => 1,
'visibility' => 4,
'type_id' => 'simple',
'attribute_set_id' => 4,
]);
$product->save();
// Retrieve product
$repository = $objectManager->get(
\Magento\Catalog\Model\ProductRepository::class
);
$retrieved = $repository->get('INT-TEST-001');
$this->assertEquals('Integration Test Product', $retrieved->getName());
$this->assertEquals(49.99, $retrieved->getPrice());
}
} Quiz
1. What does @magentoDbIsolation do?
2. How are fixtures loaded?
3. What is the test database for?
4. When does tearDown() execute?
Flashcards
Question
@magentoDbIsolation?
Click to reveal answer
Answer
Wraps test in transaction, rolls back after
Question
Load fixture annotation?
Click to reveal answer
Answer
@magentoDataFixture filename.json
Question
Integration test DB?
Click to reveal answer
Answer
Separate database for isolated testing
Question
tearDown() purpose?
Click to reveal answer
Answer
Clean up after each test method
Revision Notes
Key Takeaways
- 1. Integration tests use a separate test database
- 2. Use @magentoDbIsolation for transaction-based cleanup
- 3. Fixtures provide consistent test data
- 4. Test isolation prevents interference between tests
Interview Tips
- • Explain the difference between unit and integration tests
- • Discuss test isolation strategies
- • Know how Magento test fixtures work
Cheat Sheet
Integration Tests
- DB: separate test database
- Isolation: @magentoDbIsolation
- Fixtures: @magentoDataFixture
- Setup: setUp()/tearDown()
- Config: dev/tests/integration/