For unit tests you should use Magento\Framework\TestFramework\Unit\Helper\ObjectManager
which allows to create any object with needed arguments (if they aren't passed, mocks will be created instead).
The Magento\TestFramework\ObjectManager
is used only by integration tests and you get the PHP Fatal error: Class 'Magento\TestFramework\ObjectManager' not found
error because the different autoload.php
file should be used.
For more details, please, see the testing section from official the Magento 2 dev docs.
The short answer is, yes, I would mock the entire Context
class.
$mockScopeConfig = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class);
$mockContext = $this->getMock(\Magento\Framework\App\Helper\Context::class, [], [], '', false);
$mockContext->method('getScopeContext')->willReturn($mockScopeConfig);
$classUnderTest = new \Custom\Shipping\Helper\Config($mockContext);
A bit of background
The base of my reasoning here is that I'm assuming it is good for classes to know as little as possible about other classes, including their parents.
I'm asserting this is correct because less knowledge about other classes means less broken code due to changes in other classes.
Core parent classes that my class under test inherits from are particular problematic because I only want to test code I write (in unit tests). I don't want to test core code.
Because of that I try to keep my code as decoupled as possible from core parent classes.
This means I do not use any dependencies from parent classes, for example, by accessing their protected properties.
If I would be writing the constructor of your helper the way I would like, it would look something like this:
public function __construct(HelperContext $context, ScopeConfigInterface $scopeConfig)
{
parent::__construct($context);
$this->myScopeConfig = $scopeConfig;
}
You see, this class is only marginally coupled to the parent dependency. I don't want my class to know about the parent or its dependencies.
Unfortunately however Magento forces us to couple our class to the Context
class due to an annoying check during bin/magento setup:di:compile
(update: this check has been removed since 2.2.0).
Because of this I would write the constructor like this:
public function __construct(HelperContext $context)
{
parent::__construct($context);
$this->myScopeConfig = $context->getScopeConfig();
}
If the parent class follows the design principles tell-don't-ask, then my class will not need to call any method of the parent class. Instead, all my class will have to do is implement an abstract method which will be called by the parent at the appropriate time (in pattern lingo this is the template method pattern).
Due to the legacy of Magento 1 however not all Magento 2 code follows this principle.
So sometimes I have to mock parts of a context class or parent dependencies, even if my code doesn't need them. I do so manually though and don't rely on the unit test ObjectManager
helper.
(One example of a class that is required is entities extending from \Magento\Framework\Model\AbstractModel
.)
Conclusion
Finally, I would like to ask you why you are extending the AbstractHelper
in the first place?
Just because core modules do it like that?
Does it provide any real benefit?
If your class only requires the scope config, why not have NO parent class and just depend on the ScopeConfigInterface
directly?
That way you avoid a lot of test doubles and potential breakage when core code changes, plus you have less unnecessary overhead during runtime because all the other unneeded dependencies don't have to be instantiated.
Best Answer
You can use in your unit test an instance of
\Magento\Framework\TestFramework\Unit\Helper\ObjectManager
.Then you can use this object manager to instantiate your other class: