Як піти на тестування неін'єкційного коду?


13

Тож у мене є наступний фрагмент коду, який використовується у всій моїй системі. Зараз ми пишемо одиничні тести заднім числом (краще пізніше, ніж ніколи не було моїм аргументом), але я не бачу, як це було б перевірено?

public function validate($value, Constraint $constraint)
{
    $searchEntity = EmailAlertToSearchAdapter::adapt($value);

    $queryBuilder = SearcherFactory::getSearchDirector($searchEntity->getKeywords());
    $adapter = new SearchEntityToQueryAdapter($queryBuilder, $searchEntity);
    $query = $adapter->setupBuilder()->build();

    $totalCount = $this->advertType->count($query);

    if ($totalCount >= self::MAXIMUM_MATCHING_ADS) {
        $this->context->addViolation(
            $constraint->message
        );
    }
}

Концептуально це має застосовуватися до будь-якої мови, але я використовую PHP. Код просто створює об’єкт запиту ElasticSearch на основі Searchоб'єкта, який, в свою чергу, будується з EmailAlertоб'єкта. Ці Searchта EmailAlertі є лише POPO.

Моя проблема полягає в тому , що я не розумію , як я можу знущатися поза SearcherFactory(який використовує статичний метод), ні SearchEntityToQueryAdapter, що потрібні результати SearcherFactory::getSearchDirector і на Searchекземпляр. Як я ввожу щось, що виходить із результатів методу? Можливо, є якась модель дизайну, про яку я не знаю?

Дякуємо за будь-яку допомогу!


@DocBrown він використовується всередині $this->context->addViolationдзвінка, всередині if.
iLikeBreakfast

1
Повинно бути сліпим, вибачте.
Док Браун

Отже, всі :: є статикою?
Еван

Так, у PHP - ::це статичні методи.
Енді

@Ewan так, ::викликає статичний метод на класі.
iLikeBreakfast

Відповіді:


11

Є деякі позитиви, як знущатися над staticметодами в PHP, найкраще я використав - це бібліотека AspectMock , яку можна перенести через композитор (як знущатися над статичними методами цілком зрозуміло з документації).

Однак це проблема в останню хвилину для вирішення проблеми, яку слід виправити по-іншому.

Якщо ви все ще хочете перевірити шар, відповідальний за перетворення запитів, є досить швидкий спосіб зробити це.

Я припускаю, що зараз validateметод є частиною якогось класу, дуже швидке виправлення, яке не вимагає від вас перетворення всіх статичних викликів на виклик екземпляра, полягає в тому, щоб будувати класи, які діють як проксі для ваших статичних методів, і вводити ці проксі в класи які раніше застосовували статичні методи.

class EmailAlertToSearchAdapterProxy
{
    public function adapt($value)
    {
        return EmailAlertToSearchAdapter::adapt($value);
    }
}

class SearcherFactoryProxy
{
    public function getSearchDirector(array $keywords)
    {
        return SearcherFactory::getSearchDirector($keywords);
    }
}

class ClassWithValidateMethod
{
    private $emailProxy;
    private $searcherProxy;

    public function __construct(
        EmailAlertToSearchAdapterProxy $emailProxy,
        SearcherFactoryProxy $searcherProxy
    )
    {
        $this->emailProxy = $emailProxy;
        $this->searcherProxy = $searcherProxy;
    }

    public function validate($value, Constraint $constraint)
    {
        $searchEntity = $this->emailProxy->adapt($value);

        $queryBuilder = $this->searcherProxy->getSearchDirector($searchEntity->getKeywords());
        $adapter = new SearchEntityToQueryAdapter($queryBuilder, $searchEntity);
        $query = $adapter->setupBuilder()->build();

        $totalCount = $this->advertType->count($query);

        if ($totalCount >= self::MAXIMUM_MATCHING_ADS) {
            $this->context->addViolation(
                $constraint->message
            );
        }
    }
}

Це прекрасно! Про проксі навіть не думав. Спасибі!
iLikeBreakfast

2
Я вважаю, що у своїй книзі "Ефективна робота зі спадковим кодексом" Майкл Перо згадував про це як техніку "обгортання статики".
RubberDuck

1
@RubberDuck Я не зовсім впевнений, що його називають проксі, якщо чесно. Це те, що я називав його так довго, як я пам'ятаю його використання, ім'я містера Пера, мабуть, краще підходить, я не читав книгу, хоча.
Енді

1
Сам клас, безумовно, "проксі". Техніка розриву залежності називається "обертання статичним" IIRC. Я дуже рекомендую книгу. Він повний дорогоцінних каменів, як ви надали тут.
RubberDuck

5
Якщо ваше завдання передбачає додавання одиничних тестів до коду, настійно рекомендується книга "робота зі застарілим кодом". Його визначення "застарілого коду" - це "код без одиничних тестів", вся книга - це фактично стратегії додавання одиничних тестів до існуючого неперевіреного коду.
Етерм

4

По-перше, я б запропонував розділити це на окремі методи:

public function validate($value, Constraint $constraint)
{
    $totalCount = QueryTotal($value);
    ShowMessageWhenTotalExceedsMaximum($totalCount,$constraint);
}

private function QueryTotal($value)
{
    $searchEntity = EmailAlertToSearchAdapter::adapt($value);

    $queryBuilder = SearcherFactory::getSearchDirector($searchEntity->getKeywords());
    $adapter = new SearchEntityToQueryAdapter($queryBuilder, $searchEntity);
    $query = $adapter->setupBuilder()->build();

    return $this->advertType->count($query);
}

private function ShowMessageWhenTotalExceedsMaximum($totalCount,$constraint)
{
    if ($totalCount >= self::MAXIMUM_MATCHING_ADS) {
        $this->context->addViolation(
            $constraint->message
        );
    }
}

Це залишає вас у ситуації, коли ви можете розглянути можливість оприлюднення цих двох нових методів загальнодоступним і одиничним тестуванням QueryTotalта ShowMessageWhenTotalExceedsMaximumокремо. Тут життєздатний варіант - це взагалі не тестування одиниці QueryTotal, оскільки ви по суті перевірите лише ElasticSearch. Написання тесту для одиниць ShowMessageWhenTotalExceedsMaximumповинно бути простим і має набагато більше сенсу, оскільки це фактично перевірить вашу логіку бізнесу.

Якщо, однак, ви віддаєте перевагу тестуванню "перевірити" безпосередньо, вважаєте, що передавати саму функцію запиту як параметр у "перевірити" (зі значенням за замовчуванням $this->QueryTotal), це дозволить вам знущатися з функції запиту. Я не впевнений, чи правильно я отримав синтаксис PHP, тому, якщо цього не зробив, будь ласка, прочитайте це як "Псевдокод":

public function validate($value, Constraint $constraint, $queryFunc=$this->QueryTotal)
{
    $totalCount =  $queryFunc($value);
    ShowMessageWhenTotalExceedsMaximum($totalCount,$constraint);
}

Ідея мені подобається, але я хочу зберегти код більш об'єктно-орієнтований, а не проходити навколо таких методів.
iLikeBreakfast

@iLikeBreakfast насправді такий підхід хороший незалежно від будь-чого іншого. Метод повинен бути максимально коротким і добре робити одне і одне (дядько Боб, чистий код ). Це полегшує читання, простіше зрозуміти та простіше перевірити.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.