Test case
The Magento Functional Testing Framework supports two types of functional tests:
- Injectable test: the main type of the FTF test that uses XML data set files as inputs
- Scenario test: supports a Magento modularity and enables you to inject one step into another test
This topic discusses the injectable test only.
Test case structure
A test case class extends the Mtf\TestCase\Injectable class. It contains one test()
method and can optionally include __prepare()
, __inject()
, and tearDown()
methods.
Docblock
All out-of-the-box test cases contain description of a test flow at the beginning of a code. The description consists of the test steps and preconditions. Preconditions are the conditions to be performed before steps execution. Usage of docblock is a good practice for your new tests. See Magento\Catalog\Test\TestCase\Product\UpdateSimpleProductEntityTest class as an example.
__prepare()
(optional)
The __prepare()
method can be useful to prepare the unchangeable data that is repeatedly used for different test variations. The most popular use case is to create a fixture or a configuration setup that is used in the test.
This method is called one time only during the test launch and is optional to use. __prepare
can return an array of arguments which can be used as arguments in the test()
method of a test case and the processAssert()
method in constraints. The following example creates and returns the $customer
fixture.
1
2
3
4
5
public function __prepare(Customer $customer)
{
$customer->persist();
return ['customer' => $customer];
}
A returned argument $customer
is available in the test and in constraints.
__inject()
(optional)
The __inject()
method is used to inject data in a test (usually to initialize a page). For an example:
1
2
3
4
5
6
7
public function __inject(
CatalogProductIndex $productGrid,
CatalogProductEdit $editProductPage
) {
$this->productGrid = $productGrid;
$this->editProductPage = $editProductPage;
}
This method is run before each variation test started. Returned arguments from this method are available in constraints and in the test as well.
test()
(required)
The test()
method must contain the test steps described in a docblock. The returned arguments from this method are available in constraints. This method is run for each variation in a data set. The test()
method is required.
In the following example, the test includes preconditions and test steps. Preconditions contain a logic of different scenarios about creating a product (depending on the category state). Test steps are the following:
- opening of the product creation grid page
- searching by the
sku
parameter and opening of the product - editing of the found product
- saving of the edited product
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
/**
* Run update product simple entity test.
*
* @param CatalogProductSimple $initialProduct
* @param CatalogProductSimple $product
* @param Store|null $store
* @param string $configData
* @return array
*/
public function test(CatalogProductSimple $initialProduct, CatalogProductSimple $product, Store $store = null, $configData = '')
{
$this->configData = $configData;
// Preconditions
$initialProduct->persist();
$initialCategory = $initialProduct->hasData('category_ids')
? $initialProduct->getDataFieldConfig('category_ids')['source']->getCategories()[0]
: null;
$category = $product->hasData('category_ids') && $product->getCategoryIds()[0]
? $product->getDataFieldConfig('category_ids')['source']->getCategories()[0]
: $initialCategory;
if ($store) {
$store->persist();
$productName[$store->getStoreId()] = $product->getName();
}
$this->testStepFactory->create(
\Magento\Config\Test\TestStep\SetupConfigurationStep::class,
['configData' => $configData]
)->run();
// Steps
$filter = ['sku' => $initialProduct->getSku()];
$this->productGrid->open();
$this->productGrid->getProductGrid()->searchAndOpen($filter);
if ($store) {
$this->editProductPage->getFormPageActions()->changeStoreViewScope($store);
}
$this->editProductPage->getProductForm()->fill($product);
$this->editProductPage->getFormPageActions()->save();
return [
'category' => $category,
'stores' => isset($store) ? [$store] : [],
'productNames' => isset($productName) ? $productName : [],
];
}
A returned array is available in constraints within current variation.
tearDown()
(optional)
When constraints of the variation have been performed, you can use the tearDown()
method to get back the testing application to the initial state to be ready for the next variation execution (for example, logging out, clearing data, clearing cache).
For example, the following code deletes a sales rule after each variation:
1
2
3
4
5
6
7
public function tearDown()
{
$this->promoQuoteIndex->open();
$this->promoQuoteIndex->getPromoQuoteGrid()->searchAndOpen(['name' => $this->salesRuleName]);
$this->promoQuoteEdit->getFormPageActions()->delete();
$this->promoQuoteEdit->getModalBlock()->acceptAlert();
}
Test case flow
All data required for the test are stored in variations of a data set. A __prepare()
method is run first to prepare entities needed for a whole test. Arguments returned by a __prepare()
method are available during all test including constraints. Further, the __inject()
method injects data in the test. The test()
method performs all the test steps using the data from the variation 1
. Then, constraints listed in the variation 1
are run in the order they are listed. After that, the tearDown()
method “cleans up” to be ready for the next test or variation. When a variation fails, the test launches for the next variation in a queue.
How to create a test case
Step 1. Create a data set
Step 2. Create a PHP class in the <magento2_root_dir>/dev/tests/functional/tests/app/Magento/<module>/TestCase
directory
Step 3. Give it a name using the following format:
{action}{entityName}Entity{additional_description_if_needed}Test
For example:
- CreateConfigurableProductEntityTest
- CreateCatalogEventEntityFromCategoryPageTest
Step 4. If you have preconditions, prepare the data using a __prepare() method
Step 5. Inject the initial data for a test using a __inject() method
Step 6. Declare all the test steps in the test() method
Step 7. If you want to perform any actions after constraints, use a tearDown() method