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.
Finally i have resolve my issue .
Only Need to override Subscriber.
I am posting my entire code. may be it will helpful to someone.
di.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Newsletter\Model\Subscriber" type="Navin\Subscribe\Model\Subscriber" />
</config>
Subscriber.php Model Files
namespace Navin\Subscribe\Model;
class Subscriber extends \Magento\Newsletter\Model\Subscriber {
/**
* Initialize resource model
*
* @return void
*/
public function subscribe($email, $subscriber_name = '', $subscriber_date_of_birth = '', $subscriber_country_code = '') {
$this->loadByEmail($email);
if (!$this->getId()) {
$this->setSubscriberConfirmCode($this->randomSequence());
}
$isConfirmNeed = $this->_scopeConfig->getValue(
self::XML_PATH_CONFIRMATION_FLAG, \Magento\Store\Model\ScopeInterface::SCOPE_STORE
) == 1 ? true : false;
$isOwnSubscribes = false;
$isSubscribeOwnEmail = $this->_customerSession->isLoggedIn() && $this->_customerSession->getCustomerDataObject()->getEmail() == $email;
if (!$this->getId() || $this->getStatus() == self::STATUS_UNSUBSCRIBED || $this->getStatus() == self::STATUS_NOT_ACTIVE
) {
if ($isConfirmNeed === true) {
// if user subscribes own login email - confirmation is not needed
$isOwnSubscribes = $isSubscribeOwnEmail;
if ($isOwnSubscribes == true) {
$this->setStatus(self::STATUS_SUBSCRIBED);
} else {
$this->setStatus(self::STATUS_NOT_ACTIVE);
}
} else {
$this->setStatus(self::STATUS_SUBSCRIBED);
}
$this->setSubscriberEmail($_POST['email']);
}
if(!empty($_POST['subscriber_name']) ){
$this->setSubscriberName($_POST['subscriber_name']); //subscriber_name
$this->setSubscriberDateofbirth($_POST['subscriber_dateofbirth']); //date of birth
$this->setSubscriberCountrycode($_POST['subscriber_countrycode']); //country code
}
if ($isSubscribeOwnEmail) {
try {
$customer = $this->customerRepository->getById($this->_customerSession->getCustomerId());
$this->setStoreId($customer->getStoreId());
$this->setCustomerId($customer->getId());
} catch (NoSuchEntityException $e) {
$this->setStoreId($this->_storeManager->getStore()->getId());
$this->setCustomerId(0);
}
} else {
$this->setStoreId($this->_storeManager->getStore()->getId());
$this->setCustomerId(0);
}
$this->setStatusChanged(true);
try {
$this->save();
if ($isConfirmNeed === true && $isOwnSubscribes === false
) {
$this->sendConfirmationRequestEmail();
} else {
$this->sendConfirmationSuccessEmail();
}
return $this->getStatus();
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
}
}
Best Answer
You can try the below code and reference:
Here I have created this file in Magento root for sample add reviews.
REF URL: https://webkul.com/blog/how-to-create-product-review-rating-programatically-in-magento2/
I hope this will help.