Test abstract class php

How to Test Abstract PHP Classes With PHPUnit

How to Test Abstract PHP Classes With PHPUnit

How do you test abstract classes in PHP? Find out how to test them in this post by using PHPUnit’s getMockForAbstractClass method.

Recently, as I was working refactoring some code in a personal project I’ve been working on, I decided to change one of the classes into an abstract class. The code isn’t all the complex. It converts CSV export files from several European banks (okay, currently two) into YNAB’s import file format, which is itself a CSV file.

I wrote a class for each bank to read the respective bank’s export CSV file. Both of them extend a base reader class, called BaseReader — entirely original, no? It contains several functions which the bank-specific classes:

Given that, it made sense to me to refactor BaseReader to be an abstract class. This, for a little while, lead to what seemed like a stumbling block: How would I test an abstract class with PHPUnit?

From what I knew of PHPUnit, up until that time, at least, you couldn’t use it to mock abstract classes. Consequently, after I refactored it to be an abstract class, its functionality couldn’t be tested any longer.

As I’m a firm believer in only writing testable code, it seemed like I’d have to revert the refactor and find another approach, such as extracting the methods into a trait.

Thankfully, it turns out that PHPUnit can test abstract classes after all. After a little bit of searching I came across PHPUnit’s getMockForAbstractClass method.

Learn the Mezzio Framework

Mezzio Essentials teaches you the fundamentals of PHP’s Mezzio framework — the fundamentals that you need — to begin building get paid for applications with the Mezzio framework right away. It’s a practical, hands-on approach, which shows you just enough of about the underlying principles and concepts before stepping you through the process of creating an application. Learn More

If you have a read through the method’s documentation, you’ll see that it says:

The getMockForAbstractClass() method returns a mock object for an abstract class. All abstract methods of the given abstract class are mocked. This allows for testing the concrete methods of an abstract class.

To use it, you don’t have to do much. The following example shows all it takes.

$stub = $this->getMockForAbstractClass( BaseReader::class, [$this->root->url() . '/files/empty.csv'] ); 

The first argument is the class that you want to mock. The second is a list of arguments to its constructor. After that, you can set up any of the expected methods, their arguments, and return values, as you usually would, along with any assertions.

In Conclusion

And that’s how to test abstract classes in PHP, using PHPUnit. There’s not a lot to it, but it’s still helpful to know if and when the time comes, to test abstract classes in your code.

Have you used the method previously? Have you had any issues with it? I’d love to hear in the comments.

You might also be interested in.

Источник

Тестирование абстрактных методов

Объект абстрактного класса мы не можем создать напрямую. Поэтому работать с ним будем через stub . Что такое stub можно прочитать в статье про Модульное тестирование.

Пример абстрактного класса Abstract_Suit :

abstract class Abstract_Suit

Тестируем с помощью метода getMockForAbstractClass

use PHPUnit\Framework\TestCase; class Test_Suit extends TestCase < public function test_abstract_class() < $stub = $this->getMockForAbstractClass( 'Abstract_Suit' ); $stub->expects( $this->exactly( 1 ) ) ->method( 'abstract_method' ) ->will( $this->returnValue( 'abstract result' ) ); $this->assertSame( 'abstract result', $stub->abstract_method() ); > >

Создаем stub для абстрактного класса Abstract_Suit:

$stub = $this->getMockForAbstractClass( 'Abstract_Suit' );

Ожидаем, что будет вызван 1 раз:

$stub->expects( $this->exactly( 1 ) )

И будет возвращен результат abstract result:

->will( $this->returnValue( 'abstract result' ) );

Тестируем с помощью анонимных классов

use PHPUnit\Framework\TestCase; class Test_Suit extends TestCase < public function test_abstract_class_by_anonymous() < $suit = new class extends Abstract_Suit < public function abstract_method() < return 'rewrite abstract result'; >>; $this->assertSame( 'rewrite abstract result', $suit->abstract_method() ); > >

В целом такой код более простой для понимания, но поддерживается только с версии PHP 7.0+.

Русский разработчик со стажем. Работаю с PHP, ООП, JavaScript, Git, WordPress, Joomla, Drupal. Оптимизирую сайты под Google Page Speed, настраиваю импорты для больших магазинов на WooCommerce + WP All Import. Пишу плагины на заказ. Все мои услуги. Веду блог о разработке, дайджест в телеграмме и в ВК. Вы всегда можете нанять меня.

Источник

Testing abstract classes with PHPUnit

aside for the lack of any dependency injection possibility, it’s the factory injecting dependencies after all, writing tests for abstract classes in PHPUnit is possible but not that easy. Until now I always did it running my tests against a concrete class, a dummy one created for testing purposes, extending the abstract class I really wanted to test.

## Can I test an abstract class without extending it? Yes because I’m actually extending it mocking it.
No because I’m not really testing the class but another class extending it.

## Practical tests I wanted to test that calling the create method on a class extending the ControllersFactory class would have triggered the chain reaction resulting in a call to ControllersFactory::createInstance method.
To make things clear the chain of extensions and method calling should be

ControllersFactoryExtendingClass::create() AbstractFactory::create() ControllersFactory::createInstance() 

The test function I used to accomplish that is

public function testCreateWillCallInitAndCreateInstance() < $sut = $this->getMockForAbstractClass( // I have to use a mock generator made for abstract classes '\TAD\MVC\Factories\ControllerFactory', // the fully qualified name of the abstract class to mock array(), // no parameters for the class constructor '', // no new name for the class to mock FALSE, // do not call the original constructor TRUE, // call original clone TRUE, // call autoload array( 'createInstance' ) ); // I will mock the createInstance method $sut->expects( $this->once() )->method( 'createInstance' )->will( $this->returnValue( TRUE ) ); $sut->create( 'SomeView' ); > 

Another expectation I have is that, no matter how a class extending the ControllerFactory implements the setAndReturn method, calling create with a non-existing parameter should output something polite that can be printed on a site without making the script come to a screeching halt.

public function testCreateNonExistingControllerEchoesToScreen() < $stub = $this->getMockForAbstractClass( '\TAD\MVC\Factories\ControllerFactory', array(), '', FALSE, TRUE, TRUE, array( 'controllerExists' ) ); $stub->expects( $this->once() )->method( 'controllerExists' )->will( $this->returnValue( FALSE ) ); $this->expectOutputRegex( "/.*SomeView.*/" ); $stub->create( 'SomeView' ); > 

The abstract classes are just classes at the end of the day

Simply using the getMockForAbstractClass method will make partially or totally abstract classes testable generating, de facto, an extending class with its methods stubbed to null-returning ones. The syntax is a bit lengthy but not so different from the getMock one.

Источник

Читайте также:  Простейший кликер на python
Оцените статью