Magento 2 : When to use Object Manager in Unit tests?

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.

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.


To answer your question I will describe why ObjectManagerhelper class was developed by Magento team: When M2 development started all classes use Mage::* methods and we start increase coverage with unit test. But when we add new constructor injection many test start fails. So, we decide to introduce helper class to avoid the unneeded work.

But now constructor dependencies are stabilized, so helper is not need in most cases