Magento 2 - How to Create Unit Testing?

Previously I created an article about what is unit testing, now let's create one Helper class of a Magento 2 Module Fiko\Testing\Helper\Data and then we're going to create unit testing of that class.

1. Create Helper

You can follow to create this Helper app/code/Fiko/Testing/Helper/Data.php.

<?php

namespace Fiko\Testing\Helper;

use Magento\Store\Model\StoreManagerInterface;

class Data
{
    const BORIZQY = 'Borizqy';

    /**
     * Constructor
     *
     * @param StoreManagerInterface $storeManager
     */
    public function __construct(StoreManagerInterface $storeManager)
    {
        $this->storeManager = $storeManager;
    }

    /**
     * This method will return what's the bypassed parameter
     *
     * @param string $name
     * @return string
     */
    public function getName($name): string
    {
        return $name;
    }

    /**
     * Getting current store Id
     *
     * @return void
     */
    public function getStoreId()
    {
        return $this->storeManager->getStore()->getId();
    }

    /**
     * Checking is string parameter borizqy or not?
     *
     * @param string $surname
     * @return boolean
     */
    public function isBorizqy($surname)
    {
        return ucwords(strtolower($surname)) === self::BORIZQY;
    }
}

Let's breakdown the helper code.

  1. __construct(StoreManagerInterface $storeManager), I'm going to create constructor method so we have an instance on how to declare constructor of a module in the Unit Testing.

  2. getName($name), This example method should always return the given parameter.

  3. getStoreId(), This method should return current store view id.

  4. isBorizqy($surname), This method should validate the $surname variable passed by the argument.

2. Creating Unit Test

Once the helper class created, we need to create the Tester Class. Best practice of it are:

  1. to create append Test on each Class (for this instance, it should be DataTest)

  2. to locate the file exactly the same as the declared class under Test\Unit directory of the module. We created Fiko\Testing\Helper\Data, then we need to create Fiko\Testing\Test\Unit\Helper\Data.

We're going to create 4 testing methods, in order to create unit testing we have to:

  1. Create class and extends PHPUnit\Framework\TestCase

  2. prefix the method with test, let's say we want to create testing for isBorizqy, we can have testIsBorizqy method.

<?php

namespace Fiko\Testing\Test\Unit\Helper;

use Fiko\Testing\Helper\Data;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Model\StoreManagerInterface;
use PHPUnit\Framework\TestCase;

class DataTest extends TestCase
{
    /**
     * This method will be called on every test method
     *
     * @return void
     */
    protected function setUp(): void
    {
        $this->objectManager = new ObjectManager($this);
        $this->storeManager = $this->createMock(StoreManagerInterface::class);
        // or we can use this way:
        // $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class)
        //     ->disableOriginalConstructor()
        //     ->onlyMethods(['getStore']) // it's optional if we want to mock getStore as well
        //     ->getMockForAbstractClass();

        $this->helper = $this->objectManager->getObject(
            Data::class,
            [
                'storeManager' => $this->storeManager
            ]
        );
    }

    /**
     * Testing to get integer for getStoreId
     *
     * @return void
     */
    public function testGetStoreId(): void
    {
        $storeMock = $this->createMock(StoreInterface::class);
        $storeMock->expects($this->once())->method('getId')->willReturn(1);
        // or we can use this way:
        // $storeMock = $this->getMockBuilder(StoreInterface::class)
        //     ->disableOriginalConstructor()
        //     ->onlyMethods(['getId'])
        //     ->getMockForAbstractClass();
        // $storeMock->expects($this->once())->method('getId')->willReturn(1);

        $this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock);

        $this->assertIsInt($this->helper->getStoreId());
    }

    /**
     * Test correct name
     *
     * @param string $name
     * @param string $expected
     *
     * @return void
     * @dataProvider getNameDataProvider
     */
    public function testGetName($name, $expected): void
    {
        $this->assertSame($expected, $this->helper->getName($name));
    }

    /**
     * @return array
     * @see self::testGetName()
     */
    public function getNameDataProvider(): array
    {
        return [
            [
                'Fiko',
                'Fiko'
            ],
            [
                'Borizqy',
                'Borizqy'
            ],
        ];
    }

    /**
     * Test wrong name
     *
     * @param string $name
     * @param string $expected
     * @return void
     * @dataProvider wrongNameDataProvider
     */
    public function testWrongName($name, $expected): void
    {
        $this->assertNotSame($expected, $this->helper->getName($name));
    }

    /**
     * @return array
     * @see self::testWrongName()
     */
    public function wrongNameDataProvider(): array
    {
        return [
            [
                'Fiko Input',
                'Fiko Expected'
            ],
            [
                'Borizqy Input',
                'Borizqy Expected'
            ],
        ];
    }

    /**
     * Testing is borizqy
     *
     * @param string $surname
     * @param boolean $expected
     *
     * @return void
     * @dataProvider isBorizqyDataProvider
     */
    public function testIsBorizqy($surname, $expected)
    {
        $this->assertSame($expected, $this->helper->isBorizqy($surname));
    }

    /**
     * @return array
     * @see self::testIsBorizqy()
     */
    public function isBorizqyDataProvider()
    {
        return [
            ['Borizqy', true],
            ['borizqy', true],
            ['bORIZQY', true],
            ['BORIZQY', true],
            ['boRIZQY', true],
            ['Fiko', false],
        ];
    }
}

Let's breakdown the testing class:

  1. protected function setUp(): void, it's sort of constructor for the unit testing. This method will be called for every testing method execution. We have created 4 testing methods, means that setUp method will be called 4 times before test method.

  2. $this->objectManager->getObject(..., []), this is how we call Helper Class.

    1. first argument will be the class we want to call, in this case it is \Fiko\Testing\Helper\Data:class.

    2. second argument will be the array of the required constructor of the Helper.

  3. $this->storeManager = $this->createMock(StoreManagerInterface::class);, this is how we mocking the constructor requirement. for more information about this, please to this reference.

  4. $this->assertSame / $this->assertNotSame / $this->assertIsInt, they are logic of phpunit to validate our testing. for more information about this, please to read this reference.

3. Run Unit Test

to run unit test we can use this to execute all unit tests.

vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist

But if we want to specify which unit testing we want to test / execute, we can specify it on the command (e.g. we want to test our created helper testing):

vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist app/code/Fiko/Testing/Test/Unit/Helper/DataTest.php

or if we want to test all unit testing inside Fiko_Testing module, we can run

vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist app/code/Fiko/Testing/Test/Unit/

References:

Did you find this article valuable?

Support Fiko Borizqy by becoming a sponsor. Any amount is appreciated!