Как можно использовать PHPUnit для создания моков (заглушек) для классов внутри тестируемого класса?

Как можно замокать метод doSomething() класса ExternalHandler в PHPUnit, чтобы можно было контролировать его возвращаемое значение при тестировании метода testingMethod внутри класса MyClass?
  • 6 февраля 2024 г. 10:00
Ответы на вопрос 2
Чтобы создать мок (заглушку) для классов внутри тестируемого класса с помощью PHPUnit, можно воспользоваться функционалом его библиотеки для создания и конфигурирования мок-объектов. В вашем случае, чтобы замокать метод `doSomething()` класса `ExternalHandler`, следуйте примеру ниже:

```php
use PHPUnit\Framework\TestCase;

class MyClassTest extends TestCase
{
    public function testTestingMethod()
    {
        // Создаем мок объекта ExternalHandler
        $externalHandlerMock = $this->createMock(ExternalHandler::class);

        // Конфигурируем мок так, чтобы метод doSomething() возвращал определенное значение
        $expectedResult = 'desired_value';
        $externalHandlerMock->expects($this->any())
                            ->method('doSomething')
                            ->will($this->returnValue($expectedResult));

        // Вставляем мок в тестируемый объект, если это возможно напрямую (через конструктор, сеттер и т.п.)
        $myClass = new MyClass($externalHandlerMock);

        // Вызываем метод testingMethod и проверяем его поведение с замоканным doSomething()
        $result = $myClass->testingMethod();

        // Проверяем что результат выполнения метода соответствует ожидаемому
        $this->assertEquals('expected_result_from_testingMethod', $result);
    }
}
```

В том случае, если ваш `MyClass` создает экземпляр `ExternalHandler` внутри самого себя, например через `new ExternalHandler()`, и вы не можете внедрить зависимость напрямую, вам придется изменить дизайн `MyClass`, чтобы использовать такие паттерны, как "Factory Method" или "Dependency Injection", что делает возможным внедрение зависимостей извне.

Предположим, что `MyClass` выглядит следующим образом:

```php
class MyClass
{
    protected $externalHandler;

    public function __construct()
    {
        $this->externalHandler = new ExternalHandler();
    }

    public function testingMethod()
    {
        // Что-то делаем
        $result = $this->externalHandler->doSomething();
        // ...

        return $result; // возвращаем результат
    }
}
```

Вам нужно будет изменить `MyClass`, чтобы он принимал `ExternalHandler` через конструктор или через сеттер:

```php
class MyClass
{
    protected $externalHandler;

    public function __construct(ExternalHandler $externalHandler)
    {
        $this->externalHandler = $externalHandler;
    }

    public function testingMethod()
    {
        // Что-то делаем
        $result = $this->externalHandler->doSomething();
        // ...

        return $result; // возвращаем результат
    }
}
```
Теперь, когда `MyClass` принимает зависимость `ExternalHandler` извне, вы можете следовать первому примеру теста, чтобы замокать метод `doSomething()`.
В таком в виде как описано в вопросе - нельзя. 
Необходимо вынести создание экземпляра сторонних классов из тестируемого, и передавать их в качестве зависимостей в конструктор. Тогда их можно будет замокать.

Похожие вопросы