165

How do I test the concrete methods of an abstract class with PHPUnit?

I'd expect that I'd have to create some sort of object as part of the test. Though, I've no idea the best practice for this or if PHPUnit allows for this.

David Harkness
36.6k11 gold badges119 silver badges136 bronze badges
asked Oct 10, 2008 at 6:03
2
  • 11
    Perhaps you should consider changing the accepted answer. Commented Apr 6, 2011 at 0:23
  • 1
    Maybe stackoverflow.com/a/2947823/23963 will help. Commented Nov 29, 2013 at 1:11

6 Answers 6

270

Unit testing of abstract classes doesn't necessary mean testing the interface, as abstract classes can have concrete methods, and this concrete methods can be tested.

It is not so uncommon, when writing some library code, to have certain base class that you expect to extend in your application layer. And if you want to make sure that library code is tested, you need means to UT the concrete methods of abstract classes.

Personally, I use PHPUnit, and it has so called stubs and mock objects to help you testing this kind of things.

Straight from PHPUnit manual:

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 object give you several things:

  • you are not required to have concrete implementation of abstract class, and can get away with stub instead
  • you may call concrete methods and assert that they perform correctly
  • if concrete method relies to unimplemented (abstract) method, you may stub the return value with will() PHPUnit method
Ionuț Staicu
22.3k11 gold badges54 silver badges58 bronze badges
answered Feb 10, 2010 at 23:35
Sign up to request clarification or add additional context in comments.

Comments

60

It should be noted that as of PHP 7 support for anonymous classes has been added. This gives you an additional avenue for setting up a test for an abstract class, one that doesn't depend on PHPUnit-specific functionality.

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
}
answered Feb 20, 2018 at 15:47

1 Comment

Thank you fo this! Using an anonymous class in PHPUnit gave me a lot of flexibility in creating my various tests.
44

That's a good question. I've been looking for this too.
Luckily, PHPUnit already has getMockForAbstractClass() method for this case, e.g.

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

Important:

Note that this requires PHPUnit> 3.5.4. There was a bug in previous versions.

To upgrade to the newest version:

sudo pear channel-update pear.phpunit.de
sudo pear upgrade phpunit/PHPUnit
chiborg
28.4k15 gold badges102 silver badges120 bronze badges
answered Feb 13, 2011 at 23:46

1 Comment

Sounds interesting but you would be testing against the mock? What would the tests be like? IE: extending the mock in test case and testing against the extended test class?
1

Eran, your method should work, but it goes against the tendency of writing the test before the actual code.

What I would suggest is to write your tests on the desired functionality of a non-abstract subclass of the abstract class in question, then write both the abstract class and the implementing subclass, and finally run the test.

Your tests should obviously test the defined methods of the abstract class, but always via the subclass.

answered Nov 12, 2008 at 18:48

1 Comment

I find as an arbitrary answer: You have an abstract class 'A' having a common 'foo()' method. This 'foo()' method is being used overall all 'B', and 'C' classes, both derive from 'A'. Which class would you choose to test 'foo()'?
0

If you do not want to subclass the abstract class just to perform a unit test on the methods which are implemented in the abstract class already, you could try to see whether your framework allows you to mock abstract classes.

answered Nov 12, 2008 at 19:06

Comments

0

Nelson's answer is wrong.

Abstract classes don't require all of their methods to be abstract.

The implemented methods are the ones we need to test.

What you can do is create a fake stub class on the unit test file, have it extend the abstract class and implement only what's required with no functionality at all, of course, and test that.

Cheers.

answered Sep 23, 2009 at 15:19

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.