I really like automated tests in Magento 2. They make everyone’s life easier and greatly contribute to overall product quality. Integration tests are an important part of Magento 2’s testing suite.
Integration testing
So far integration tests seem very interesting to me and I think that they make a lot of sense in Magento 2. There are also functional and unit tests that are also important. It’s just that I think that integration tests are really useful in many cases.
The main purpose of integration tests is to find out how different modules work together. I wrote a bit about integration tests in general here.
One of the php programming language features that is used in Magento 2 integrations tests are annotations.
Annotations
In php, annotations are meta data that are used to inject some behaviour. This is achieved by placing that data into the comments above the structure they are referring to.
Some people from the php community support annotations while others are against them. Clearly this approach has gained much popularity in the last few years and some of the leading projects such as Symfony and Doctrine use them. I think their usefulness is explained nicely here.
Annotations in Magento 2 Integration testing framework
Let’s take a look at one of the tests from Magento core testsuite.
dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php
/** * Disabled form security in order to prevent exit from the app * @magentoConfigFixture current_store admin/security/session_lifetime 59 */ public function testIsLoggedInWithIgnoredLifetime() { $this->_auth->login( \Magento\TestFramework\Bootstrap::ADMIN_NAME, \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD ); $this->assertTrue($this->_model->isLoggedIn()); $this->_model->setUpdatedAt(time() - 101); $this->assertTrue($this->_model->isLoggedIn()); }
The purpose of this test is to check if the logged in user is logged out after some period of time. We need to set the configuration value before this test and we do this with magentoConfigFixture annotation. Explanation follows.
How annotations work
Integration tests in Magento 2 are based on PHPUnit.
Annotations from docblocks are internally gathered with PHP’s reflection API’s getDocComment method which has the ability to introspect the method and return it’s doc block (where the annotations are). PHPUnit uses this functionality with getAnnotations method that returns the annotations as array.
There is a feature in PHPUnit that is used for tracking events that occur during tests execution. Events such as onTestStart, onTestSuccess, onTestFailure, etc. The class that makes this possible is PHPUnit_Framework_TestListener.
Magento has a wrapper event class over PHPUnit_Framework_TestListener that fires it’s own events and those call specific annotation classes that are registered as listeners.
In our example @magentoConfigFixture is used to set the config value. The Annotation class ConfigFixture from Magento\TestFramework\Annotation is registered as a listener. Once the test runs, the startTest method from that class will be called and set configuration values:
/var/www/html/magento2/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php
/** * Handler for 'startTest' event * * @param \PHPUnit_Framework_TestCase $test */ public function startTest(\PHPUnit_Framework_TestCase $test) { $this->_currentTest = $test; $this->_assignConfigData($test); }
Annotations
@magentoConfigFixture
As mentioned, you can use this annotation when you need to set some config value before the test starts. The format for this annotation is:
* @magentoConfigFixture store_code section/group/field value
It will set the value for the configuration field with the path section/group/field for store that has the “store_code”.
@magentoDbIsolation
I think this one is very useful. When enabled, each of your tests will be wrapped inside a transaction and rolled back afterwards.
* @magentoDbIsolation enabled
You can enable it when you want to make sure that your tests don’t affect each other.
When it’s disabled you will be able to check the values in database after the tests ran which makes debugging easier.
This feature can be used on class level and on method level.
@magentoDataFixture
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
This annotation is used when you want to run your custom code before the test. This can be any piece of code contained in a script.
You can also provide a rollback script that is supposed to revert the changes that your fixture made after the test has finished. These are suffixed with “_rollback”.
This fixture adds a product and removes it on rollback:
dev/tests/integration/testsuite/Magento/Catalog/_files/product_special_price.php
<?php /** * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ /** @var $product \Magento\Catalog\Model\Product */ $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); $product->setTypeId('simple') ->setAttributeSetId(4) ->setWebsiteIds([1]) ->setName('Simple Product') ->setSku('simple') ->setPrice(10) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setStockData(['use_config_manage_stock' => 0]) ->setSpecialPrice('5.99') ->save();
dev/tests/integration/testsuite/Magento/Catalog/_files/product_special_price_rollback.php
<?php /** * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ /** @var \Magento\Framework\Registry $registry */ $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry'); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( 'Magento\Catalog\Model\ProductRepository' ); try { $product = $repository->get('simple'); $product->delete(); } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { //Entity already deleted } $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false);
@magentoAppIsolation
When this annotation is enabled, the application will be restarted on each test run.
@magentoCache
Simply determines whether the cache is enabled or disabled. Useful for many scenarios.
@magentoComponentsDir
Includes all the files from the specific directory. This fixture is usually used for adding email templates to the test.
@magentoAdminConfigFixture
Similar to
@magentoAppArea
Defines application area in cases where this is relevant.
@magentoDataFixtureBeforeTransaction
These fixtures will be executed, as the name suggests, before transaction that is enabled by magentoDbIsolation so they won’t be affected by that.
Conclusion
As you can see, annotations in Magento 2 integration testing framework provide all kinds of useful features. You can get a better insight of them by looking at the existing integration tests. Let me know what you think.
Would you like to recommend this article to your friends or colleagues?
Feel free to share.