How to Mock Final Classes?
How to mock classes that are defined as final or some of their methods are final?
Mocking means replacing the original object with its testing imitation that does not perform any functionality and just looks like the original object. And pretending the behavior we need to test.
For example, instead of a PDO with methods like query() etc., we create a mock that pretends working with the database, and instead verifies that the correct SQL statements are called, etc. More e.g. in the Mockery documentation.
And in order to be able to pass mock to methods that use
type hint, it is necessary for the mock class to inherit from the PDO. And that
can be a stumbling block. If the PDO or method query() were final, it would not
Is there any solution? The first option is not to use the final keyword at all. This, of course, does not help with the third-party code that it uses, but mainly detracts from the important element of the object design. For example, there is dogma that every class should be either final or abstract.
The second and very handy option is to use BypassFinals, which removes finals from source code on-the-fly and allows mocking of final methods and classes.
Install it using Composer:
composer require dg/bypass-finals --dev
And just call at the beginning of the test:
require __DIR__ . '/vendor/autoload.php'; DG\BypassFinals::enable();
Thats all. Incredibly black magic
BypassFinals requires PHP version 5.6 and supports PHP up to 7.2. It can be used together with any test tool such as PHPUnit or Mockery.
This functionality is directly implemented in the „Nette Tester“: https://tester.nette.org version 2.0 and can be enabled this way:
require __DIR__ . '/vendor/autoload.php'; Tester\Environment::bypassFinals();