Skip to main content

Mocking Final & Private Methods

Mocking Final & Private Methods

Unitary doesn’t support mocking final or private methods — by design.

This isn’t a technical limitation, it’s an intentional choice. Mocking in Unitary is meant to reflect how your system is structured, not bypass it.


Focus on Testable Design

Unitary encourages testing behavior, not internal implementation details. That means relying on public methods and substitutable dependencies.

If you find yourself needing to mock a private or final method, it's often a sign the design could be improved.


Use Interfaces Instead of Final Classes or Methods

Final classes and methods can’t be mocked in PHP, not in Unitary or any other framework. But if you rely on an interface instead of the concrete class, mocking becomes straightforward.

Why Interfaces Help

Interfaces describe behavior without tying you to an implementation. If your class depends on an interface, you can easily replace it in tests, no matter how the real class is built.

Example

interface MailerInterface {
public function send(string $to, string $subject, string $body): bool;
}

Even if the real mailer is final:

final class Mailer implements MailerInterface {
public function send(string $to, string $subject, string $body): bool
{
// Implementation details
}
}

Your application can remain flexible:

class NotificationService {
public function __construct(private MailerInterface $mailer) {}

public function sendWelcome(string $email): bool {
return $this->mailer->send($email, 'Welcome', 'Thanks for signing up.');
}
}

Then in you test, just mock the interface:

group('Sends welcome email', function ($test) {
$mailer = $test->mock(MailerInterface::class, function ($mock) {
$mock->method('send')->willReturn(true)->called(1);
});

$service = new NotificationService($mailer);
$result = $service->sendWelcome('john@example.com');

$test->validate($result, fn($v) => $v->isTrue());
});

You’re testing behavior, not implementation. That’s the goal.


Why You Shouldn't Mock Private Methods

Private methods are internal details. They aren't meant to be called directly, and they aren’t meant to be mocked.

If a private method contains logic important enough to test, consider:

  • Moving it to its own class
  • Making it public or protected in a dedicated helper
  • Rethinking its role in the design

The aim is to keep logic visible and testable.


If mocking feels difficult, the code may need a small design adjustment. Unitary supports clear, testable architecture, use interfaces, inject dependencies, and focus on behavior.