Magento 2 – Correct Way to Create Mock Object in Unit Test

magento2magento2.2phpunittestingunit tests

Now days I am focusing on TDD development, and I am following the Magento test class for reference. So firstly I look into Contact module (as it is the small module) and I found that in DataTest.php from Magento\Contact\Test\Unit\Helper\DataTest.php it gets the mock object from construct argument. like in setUp you can find it like,

  protected function setUp()
    {
        $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
        $className = \Magento\Contact\Helper\Data::class;
        $arguments = $this->objectManagerHelper->getConstructArguments($className);
        /**
         * @var \Magento\Framework\App\Helper\Context $context
         */
        $context = $arguments['context'];
        $this->scopeConfigMock = $context->getScopeConfig();
        $this->customerSessionMock = $arguments['customerSession'];
        $this->customerViewHelperMock = $arguments['customerViewHelper'];
        $this->helper = $this->objectManagerHelper->getObject($className, $arguments);
    }

from here customersession Magento get the mock object from construct argument.

        `$this->customerSessionMock = $arguments['customerSession'];`

But in some of test class magento create the mock object by using createMock method. Like,

$this->customerSessionMock = $this->createMock(\Magento\Customer\Model\Session::class);

So which way I should follow to create mock object ? and why?

Best Answer

The unit test object manager helper has been introduced to make it easier to create tests for legacy classes with lots of dependencies, automatically mocking all the dependencies. Avoid using it for new code, instantiate the class under test directly and mock interfaces instead of concrete classes wherever possible.

createMock has been introduced in PHPUnit 6, and Magento 2.2 finally upgraded to that version. It also automatically mocks (actually: stubs) return values of methods if not specified otherwise, so that the unit test object manager helper is not necessary anymore.

If you write code for Magento 2.1 and must support PHPUnit 4, the preferred way to create mocks is:

$this->getMockBuilder()->getMock(\Magento\Customer\Model\Session::class);

The old $this->getMock() does not exist anymore in PHPUnit 6

Example

Today, the Magento\Contact\Test\Unit\Helper\DataTest set up that you showed would look like this:

protected function setUp()
{
    /*
     * First, create all mocks where we will define expectations later in the tests
     */
    $this->scopeConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class);
    $this->customerSessionMock = $this->createMock(\Magento\Customer\Model\Session::class);
    $this->customerViewHelperMock = $this->createMock(\Magento\Customer\Helper\View::class);

    /*
     * Then prepare the context object which contains one of the mocks
     */
    $context = $this->createMock(\Magento\Framework\App\Helper\Context::class);
    $context->method('getScopeConfig')->willReturn($this->scopeConfigMock);

    /*
     * Finally instantiate the class under test and pass all the dependencies explicitly
     */
    $this->helper = new \Magento\Contact\Helper\Data(
        $context,
        $session,
        $customerViewHelper
    );
}