Disable Module in integration tests / how is the sandbox config.php written
Since this is still relevant in 2020: you can disable modules during the integration tests' install.
bin/magento setup:install
accepts --disable-modules
, which you can use in /dev/tests/integration/etc/install-config-mysql.php
In the example below I'm disabling all the MSI modules. Which is useful if you for some reason can't replace those modules with composer replace (as described here https://www.integer-net.com/make-magento-2-small-again/).
return [
'db-host' => 'db',
'db-user' => 'root',
'db-password' => 'magento',
'db-name' => 'magento_integration_tests',
'db-prefix' => '',
'backend-frontname' => 'backend',
'admin-user' => \Magento\TestFramework\Bootstrap::ADMIN_NAME,
'admin-password' => \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD,
'admin-email' => \Magento\TestFramework\Bootstrap::ADMIN_EMAIL,
'admin-firstname' => \Magento\TestFramework\Bootstrap::ADMIN_FIRSTNAME,
'admin-lastname' => \Magento\TestFramework\Bootstrap::ADMIN_LASTNAME,
'es-hosts' => 'elasticsearch:9200',
'disable-modules' => join(
',',
[
'Magento_Inventory',
'Magento_InventoryApi',
'Magento_InventoryCatalogApi',
'Magento_InventoryConfigurationApi',
'Magento_InventorySales',
'Magento_InventoryMultiDimensionalIndexerApi',
'Magento_InventoryReservationsApi',
'Magento_InventoryIndexer',
'Magento_InventorySalesApi',
'Magento_InventorySourceDeductionApi',
'Magento_InventorySourceSelectionApi'
]
),
];
The key disable-modules
accepts a comma-separated list, which I've entered as a joined array which is easier to read/edit.
As I stated in my comment: it seems that Magento 2 enabled all modules by default when running an integration test. Why this is I don't know. I've already filed an issue for this since I think it's unexpected behavior.
As a (hopefully temporary) workaround for this I came up with a solution where I created a wrapper around the bootstrap (call it a "bootwrap" if you may).
So in my phpunit.xml
, I set my boostrap to:
bootstrap="./etc/bootstrap.php"
And etc/bootstrap.php
looks like this:
// Bootstrap wrapper, used to disable certain modules for integration tests.
$ds = DIRECTORY_SEPARATOR;
// Default bootstrap, as provided by Magento:
include_once __DIR__ . $ds . implode($ds, ['..', 'framework', 'bootstrap.php']);
// Copy the original config.php, that has all the disabled modules:
$tmpDir = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getAppTempDir();
$dir = __DIR__ . $ds . implode($ds, ['..', '..', '..', '..', 'app', 'etc']);
copy(
$dir . $ds . 'config.php',
$tmpDir . $ds . implode($ds, ['etc', 'config.php'])
);
Basically, the only thing it does, is copy your app/etc/config.php
to your dev/tests/integration/tmp/xxxxxx/etc
-folder; effectively disabling the proper modules.
Update:
It looks like the above fix no longer works in Magento 2.2.5.
I have "fixed" it for now with yet another dirty hack. Instead of having to copy/paste the original config.php
before my tests, I had to dig deeper into the bootstrapping-context.
So I copied the original framework/bootstrap.php
to my etc.php
and edited as follows:
- I changed the
require_once __DIR__ ...
-statements torequire_once __DIR__ . '/../framework'
so all required files got loaded properly. - I replaced the
$application
-instantiating as follows:
Original:
$application = new \Magento\TestFramework\Application(
$shell,
$installDir,
$installConfigFile,
$globalConfigFile,
$settings->get('TESTS_GLOBAL_CONFIG_DIR'),
$settings->get('TESTS_MAGENTO_MODE'),
AutoloaderRegistry::getAutoloader(),
true
);
My version:
// Manipulate existing application class to inject the projects' config.php:
$application = new class(
$shell,
$installDir,
$installConfigFile,
$globalConfigFile,
$settings->get('TESTS_GLOBAL_CONFIG_DIR'),
$settings->get('TESTS_MAGENTO_MODE'),
AutoloaderRegistry::getAutoloader(),
true
) extends \Magento\TestFramework\Application
{
/**
* @inheritDoc
*/
public function install($cleanup)
{
$this->_ensureDirExists($this->installDir);
$this->_ensureDirExists($this->_configDir);
$file = $this->_globalConfigDir . '/config.php';
$targetFile = $this->_configDir . str_replace($this->_globalConfigDir, '', $file);
$this->_ensureDirExists(dirname($targetFile));
if ($file !== $targetFile) {
copy($file, $targetFile);
}
parent::install($cleanup);
}
/**
* @inheritDoc
*/
public function isInstalled()
{
// Always return false, otherwise DB credentials will be empty
return false;
}
};
I had to use an anonymous class because the new
instantiation of the Application
prevented me from using proper dependency injection. What it basically does, is it copies the config.php
-file before the original install()
-method is called. So when a native installation starts, it uses the config.php
of my project to determine which modules should be installed.
Also, I had to override the isInstalled()
-method to always return false, because otherwise the database function would have the wrong credentials (isInstalled()
would always return true
if a config.php
is found).
So this way the integration tests on our CI server will run on an installation that reflects our production setup.