Creating Integration Tests for Magento 2 Modules
This works for us but we haven't yet looked into moving them to a separate location to address 6.)
1.) Place your integration tests under dev/tests/integration/testsuite/Vendor
2.) copy dev/tests/integration/phpunit.dist.xml
to
dev/tests/integration/phpunit.xml
and replace
<directory suffix="Test.php">testsuite</directory>
<directory suffix="Test.php">../../../update/dev/tests/integration/testsuite</directory>
<exclude>testsuite/Magento/Test/Integrity</exclude>
<exclude>testsuite/Magento/MemoryUsageTest.php</exclude>
with
<directory suffix="Test.php">testsuite/Vendor</directory>
3.) run it ../../../vendor/bin/phpunit
or with ../../../vendor/bin/phpunit path/to/tests
from the dev/test/integration folder
Please note that the integration tests take longer than 15 seconds, at least on first run as it essentially installs Magento. You can save on subsequent runs if you use
<const name="TESTS_CLEANUP" value="disabled"/>
in your phpunit.xml
I have played a little bit with the integration tests, and this is what I found so far.
Basically, I've followed similar steps to what Fooman said, with some differences in order to make the integration test be part of my module.
These are the steps I followed:
1- Place your integration tests under app/code/Vendor/CustomModule/Test/Integration
2- Copy dev/tests/integration/phpunit.dist.xml
to dev/tests/integration/phpunit.xml
and replace
<testsuite name="Magento Integration Tests">
<directory suffix="Test.php">testsuite</directory>
<directory suffix="Test.php">../../../update/dev/tests/integration/testsuite</directory>
<exclude>testsuite/Magento/Test/Integrity</exclude>
<exclude>testsuite/Magento/MemoryUsageTest.php</exclude>
</testsuite>
with
<testsuite name="Magento Integration Tests">
<directory suffix="Test.php">../../../app/code/Vendor/CustomModule/Test/Integration</directory>
</testsuite>
3- Then I run it using the CLI tool bin/magento dev:test:run integration
You should have in mind what Fooman says about the "TESTS_CLEANUP" and the time it takes to setup the integration tests if you have the cleanup enable.
Here I add a functional example for further reference. You will see how you can access the object manager, and generate instance of Magento classes, as well as using Magento fixtures.
app/code/Vendor/CustomModule/Controller/Order/Info.php
namespace Vendor\CustomModule\Controller\Order;
use Magento\Framework\Controller\ResultFactory;
class Info
extends \Magento\Framework\App\Action\Action
{
/**
* @param \Magento\Framework\App\Action\Context $context
* @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
*/
public function __construct(
\Magento\Framework\App\Action\Context $context,
\Magento\Sales\Api\OrderRepositoryInterface $orderRepository
)
{
$this->orderRepository = $orderRepository;
parent::__construct($context);
}
/**
* Return Json OrderInfo
*
* @return \Magento\Framework\Controller\Result\Json $this
*/
public function execute()
{
$orderId = $this->getRequest()->getParam('id');
$order = $this->orderRepository->get($orderId);
$orderInfo = [
'total' => $order->getBaseGrandTotal()
];
/** @var \Magento\Framework\Controller\Result\Json $result */
$result = $this->resultFactory->create(ResultFactory::TYPE_JSON);
return $result->setData($orderInfo);
}
}
app/code/Vendor/CustomModule/etc/frontend/routes.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="vendor_custommodule" frontName="vendor_custommodule">
<module name="Vendor_CustomModule"/>
</route>
</router>
</config>
app/code/Vendor/CustomModule/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Vendor_CustomModule" setup_version="1.0.0">
<sequence>
<module name="Magento_Sales" />
</sequence>
</module>
</config>
app/code/Vendor/CustomModule/Test/Integration/Controller/Order/InfoTest.php
namespace Vendor\CustomModule\Controller\Order;
use Magento\TestFramework\TestCase\AbstractController;
class InfoTest extends AbstractController
{
public function getOrderInfoActionDataProvider()
{
return [
'order with one simple item' => [
'incrementId' => '100000001',
'contentType' => 'application/json',
'orderTotal' => 100
]
];
}
/**
* @dataProvider getOrderInfoActionDataProvider
* @magentoDataFixture Magento/Sales/_files/order.php
*/
public function testOrderInfoAction($incrementId, $expectedContentType, $expectedOrderTotal)
{
/** @var $objectManager \Magento\TestFramework\ObjectManager */
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
/** @var \Magento\Sales\Model\OrderFactory $orderFactory */
$orderFactory = $objectManager->get('Magento\Sales\Model\OrderFactory');
$order = $orderFactory->create();
$order->loadByIncrementId($incrementId);
$this->dispatch("vendor_custommodule/order/info/id/{$order->getId()}");
$contentType = $this->getResponse()->getHeader('Content-Type');
$this->assertEquals($expectedContentType, $contentType->getFieldValue());
$responseJson = $this->getResponse()->getBody();
$responseArray = json_decode($responseJson, true);
$this->assertEquals($expectedOrderTotal, $responseArray['total']);
}
}
app/code/Vendor/CustomModule/registration.php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Vendor_CustomModule',
__DIR__
);
I've successfully placed my integration tests into a separate directory: src/My/Module/test/integration
. It could be any other directory as well, like app/code/My/Module/Test
.
Add them as new test suite to the Magento integration tests: Copy dev/tests/integration/phpunit.xml.dist
to dev/tests/integration/phpunit.xml
and add the following in the <testsuites>
node:
<testsuite name="My_Module">
<directory suffix="Test.php">../../../src/My/Module/test</directory>
</testsuite>
Then run the tests like this from the dev/tests/integration
directory:
../../../vendor/bin/phpunit --testsuite "My_Module"
With the --testsuite
parameter you can select one test suite by name, so that not all integration tests are run at once
Update: Fixtures
To use own fixtures, a little workaround was necessary, because in Magento\TestFramework\Annotation
the fixture base directory is defined globally. But fortunately Magento allows method names as fixtures as well, so the following works:
/**
* @magentoDataFixture loadFixture
*/
public function testSomething()
{
}
public static function loadFixture()
{
include __DIR__ . '_files/something_fixture.php';
}