Magento 2.1.18 is the final 2.1.x release. After June 2019, Magento 2.1.x will no longer receive security patches, quality fixes, or documentation updates.
To maintain your site's performance, security, and PCI compliance, upgrade to the latest version of Magento.

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.

Test case flow diagram

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

Updated