Testowanie klas abstrakcyjnych

144

Jak przetestować konkretne metody klasy abstrakcyjnej za pomocą PHPUnit?

Spodziewałbym się, że będę musiał stworzyć jakiś obiekt w ramach testu. Chociaż nie mam pojęcia, jaka jest najlepsza praktyka w tym zakresie lub czy PHPUnit na to pozwala.

Mez
źródło
10
Być może powinieneś rozważyć zmianę zaakceptowanej odpowiedzi.
Jacob
1
Może stackoverflow.com/a/2947823/23963 pomoże.
Nigel Thorne

Odpowiedzi:

240

Testowanie jednostkowe klas abstrakcyjnych nie musi oznaczać testowania interfejsu, ponieważ klasy abstrakcyjne mogą mieć konkretne metody, a te konkretne metody można testować.

Podczas pisania kodu biblioteki nie jest tak rzadkie, że istnieje pewna klasa bazowa, którą można rozszerzyć w warstwie aplikacji. A jeśli chcesz mieć pewność, że kod biblioteki jest testowany, potrzebujesz środków do UT konkretnych metod klas abstrakcyjnych.

Osobiście używam PHPUnit i ma on tak zwane obiekty pośredniczące i makiety, które pomagają w testowaniu tego rodzaju rzeczy.

Prosto z instrukcji PHPUnit :

abstract class AbstractClass
{
    public function concreteMethod()
    {
        return $this->abstractMethod();
    }

    public abstract function abstractMethod();
}

class AbstractClassTest extends PHPUnit_Framework_TestCase
{
    public function testConcreteMethod()
    {
        $stub = $this->getMockForAbstractClass('AbstractClass');
        $stub->expects($this->any())
             ->method('abstractMethod')
             ->will($this->returnValue(TRUE));

        $this->assertTrue($stub->concreteMethod());
    }
}

Mock obiekt daje kilka rzeczy:

  • nie musisz mieć konkretnej implementacji klasy abstrakcyjnej i zamiast tego możesz uciec od stubu
  • możesz wywołać konkretne metody i zapewnić, że działają poprawnie
  • jeśli konkretna metoda opiera się na niezaimplementowanej (abstrakcyjnej) metodzie, możesz zablokować zwracaną wartość metodą will () PHPUnit
Victor Farazdagi
źródło
38

To dobre pytanie. Też tego szukałem.
Na szczęście PHPUnit ma już getMockForAbstractClass()metodę na ten przypadek, np

protected function setUp()
{
    $stub = $this->getMockForAbstractClass('Some_Abstract_Class');
    $this->_object = $stub;
}

Ważny:

Zauważ, że wymaga to PHPUnit> 3.5.4. W poprzednich wersjach wystąpił błąd .

Aby zaktualizować do najnowszej wersji:

sudo pear channel-update pear.phpunit.de
sudo pear upgrade phpunit/PHPUnit
takeshin
źródło
Brzmi interesująco, ale testowałbyś na próbach? Jak wyglądałyby testy? IE: rozszerzenie makiety w przypadku testowym i testowanie pod kątem rozszerzonej klasy testowej?
stefgosselin
34

Należy zauważyć, że od PHP 7 dodano obsługę klas anonimowych . Daje to dodatkową możliwość skonfigurowania testu dla klasy abstrakcyjnej, takiej, która nie zależy od funkcji specyficznych dla PHPUnit.

class AbstractClassTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @var AbstractClass
     */
    private $testedClass;

    public function setUp()
    {
        $this->testedClass = new class extends AbstractClass {

            protected function abstractMethod()
            {
                // Put a barebones implementation here
            }
        };
    }

    // Put your tests here
}
Gordon M.
źródło
4
Dziękuję za to! Używanie anonimowej klasy w PHPUnit dało mi dużą elastyczność w tworzeniu różnych moich testów.
Alice Wonder
1

Eran, twoja metoda powinna działać, ale jest sprzeczna z tendencją pisania testu przed rzeczywistym kodem.

Sugerowałbym, aby napisać testy na pożądanej funkcjonalności nieabstrakcyjnej podklasy danej klasy abstrakcyjnej, a następnie napisać zarówno klasę abstrakcyjną, jak i podklasę implementującą, a na koniec uruchomić test.

Twoje testy powinny oczywiście testować zdefiniowane metody klasy abstrakcyjnej, ale zawsze za pośrednictwem podklasy.


źródło
Jako arbitralna odpowiedź znajduję: masz abstrakcyjną klasę „A” mającą wspólną metodę „foo ()”. Ta metoda „foo ()” jest używana ogólnie wszystkie klasy „B” i „C”, obie wywodzą się z klasy „A”. Którą klasę wybrałbyś do testowania funkcji „foo ()”?
user3790897
1

Odpowiedź Nelsona jest błędna.

Klasy abstrakcyjne nie wymagają, aby wszystkie ich metody były abstrakcyjne.

Zaimplementowane metody to te, które musimy przetestować.

To, co możesz zrobić, to utworzyć fałszywą klasę pośredniczącą w pliku testu jednostkowego, rozszerzyć klasę abstrakcyjną i zaimplementować tylko to, co jest wymagane, bez żadnej funkcjonalności, oczywiście, i przetestować to.

Twoje zdrowie.

skqr
źródło
0

Jeśli nie chcesz tworzyć podklasy klasy abstrakcyjnej tylko po to, aby wykonać test jednostkowy metod, które są już zaimplementowane w klasie abstrakcyjnej, możesz spróbować sprawdzić, czy Twój framework pozwala na mockowanie klas abstrakcyjnych.

hangy
źródło