Enjekte edilemeyen kodu nasıl test edersiniz?


13

Bu yüzden tüm sistemde aşağıdaki kod parçası var. Şu anda birim testleri geriye dönük olarak yazıyoruz (benim argümanımdan daha geç değil), ama bunun nasıl test edilebileceğini anlamıyorum?

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
        );
    }
}

Kavramsal olarak bu herhangi bir dile uygulanabilir olmalıdır, ancak PHP kullanıyorum. Kod basitçe bir Searchnesneye dayalı olarak bir ElastikArama sorgu nesnesi oluşturur ve bu da bir EmailAlertnesneden oluşur. Bunlar Searchve EmailAlertsadece POPO'lar.

Benim sorunum dışarı taklit nasıl kalmamasıdır SearcherFactory(statik yöntemini kullanır), ne de SearchEntityToQueryAdapterelde edilen sonuçları ihtiyacı olan SearcherFactory::getSearchDirector veSearch örneği. Bir yöntemdeki sonuçlardan oluşturulan bir şeyi nasıl enjekte edebilirim? Belki farkında olmadığım bir tasarım deseni var mı?

Herhangi bir yardım için teşekkürler!


@DocBrown $this->context->addViolationçağrının içinde, içinde kullanılıyor if.
iLikeBreakfast

1
Kör olmalı, üzgünüm.
Doc Brown

Yani tüm :: statik?
Ewan

Evet, PHP'de ::statik yöntemler içindir.
Andy

@Ewan yes, ::sınıfta statik bir yöntem çağırır.
iLikeBreakfast

Yanıtlar:


11

Bazı posibilites vardır, nasıl staticPHP yöntemleri alay , kullandığım en iyi çözüm besteci aracılığıyla çekilebilir AspectMock kütüphanesi (statik yöntemler alay nasıl belgelerden oldukça anlaşılır).

Ancak, farklı bir şekilde düzeltilmesi gereken bir sorun için son dakika düzeltmesi.

Sorguları dönüştürmekten sorumlu katmanı birim olarak test etmek istiyorsanız, bunu yapmanın oldukça hızlı bir yolu vardır.

Şu anda validateyöntemin bazı sınıfın bir parçası olduğunu varsayıyorum , tüm statik çağrılarınızı örnek çağrısına dönüştürmenizi gerektirmeyen çok hızlı düzeltme, statik yöntemleriniz için proxy olarak hareket eden sınıflar oluşturmak ve bu proxy'leri sınıflara enjekte etmektir. statik yöntemleri daha önce kullandı.

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
            );
        }
    }
}

Bu harika! Vekilleri bile düşünmedim. Teşekkürler!
iLikeBreakfast

2
Michael Feather'ın buna "Eski Kodla Etkili Çalışma" kitabında "Wrap Statik" tekniği denildiğine inanıyorum.
RubberDuck

1
@RubberDuck Dürüst olmak gerekirse, proxy olarak adlandırıldığından tamamen emin değilim. Onu hatırlayabildiğim sürece buna denirdim, Bay Feather'ın adı muhtemelen daha uygun, yine de kitabı okumadım.
Andy

1
Sınıfın kendisi kesinlikle bir "vekil" dir. Bağımlılık kırma tekniğine "sargı statik" IIRC denir. Kitabı tavsiye ederim. Burada sağladığınız gibi mücevherlerle dolu.
RubberDuck

5
İşiniz koda birim testleri eklemeyi içeriyorsa, "eski kodla çalışma" önemle tavsiye edilen bir kitaptır. Onun "eski kod" tanımı "birim testleri olmayan kod", tüm kitap aslında test edilmemiş mevcut koda birim testleri eklemek için stratejiler.
Eterm

4

İlk olarak, bunu ayrı yöntemlere ayırmanızı öneririm:

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
        );
    }
}

Bu, bu iki yeni yöntemler kamu ve birim test yapmak için düşünebilirsiniz bir durumda bırakır QueryTotalve ShowMessageWhenTotalExceedsMaximumbireysel olarak. Burada geçerli bir seçenek aslında birim testi yapmak değildirQueryTotal , çünkü sadece Elastik Arama'yı test edersiniz. Bir birim test yazmak ShowMessageWhenTotalExceedsMaximumkolay olmalı ve iş mantığınızı gerçekten test edebileceğinden çok daha mantıklı olmalıdır.

Ancak, "validate" öğesini doğrudan test etmeyi tercih ederseniz, sorgu işlevinin kendisini "validate" (varsayılan değeri olan $this->QueryTotal) içine bir parametre olarak geçirmeyi düşünün , bu sorgu işlevini alay etmenizi sağlar. Ben PHP sözdizimi doğru var emin değilim, bu yüzden ben yapmadım, lütfen "Sözde kod" olarak okuyun:

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

Fikri seviyorum, ama bunun gibi yöntemler dolaşmak yerine kodu daha nesne yönelimli tutmak istiyorum.
iLikeBreakfast

@iLikeBreakfast aslında bu yaklaşım başka bir şey ne olursa olsun iyidir. Bir yöntem mümkün olduğunca kısa olmalı ve bir şey ve bir şey iyi yapmalıdır (Bob Amca, Temiz Kod ). Bu, okumayı, anlaşılmasını ve test edilmesini kolaylaştırır.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.