Search icon
Subscription
0
Cart icon
Close icon
You have no products in your basket yet
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Instant Hands-on Testing with PHPUnit How-to

You're reading from  Instant Hands-on Testing with PHPUnit How-to

Product type Book
Published in May 2013
Publisher Packt
ISBN-13 9781782169581
Pages 82 pages
Edition 1st Edition
Languages
Author (1):
Michael Lively Michael Lively
Profile icon Michael Lively

Testing protected and private methods (Intermediate)


A common question of those that are getting started with unit testing is, how are protected and private methods tested? Protected and private methods are not uncommon and the desire to test the code in them should be natural. The confusion that arises from how to test these methods is created at least in part by the thought that they must be tested independently.

In the book Pragmatic Unit Testing, Dave Thomas and Andy Hunt had this to say:

In general, you don't want to break any encapsulation for the sake of testing (or as mom used to say, "don't expose your privates!"). Most of the time, you should be able to test a class by exercising its public methods. If there is significant functionality that is hidden behind private or protected access, that might be a warning sign that there's another class in there struggling to get out.

Using the public interface of your class is by far the best way to test protected and private methods. If you find yourself unable to do this, PHPUnit and PHP itself offer solutions to test these methods directly.

How to do it...

The following code in the CardCollection class is used to add a card to the collection:

<?php
class CardCollection implements IteratorAggregate
{
  // ...
  public function addCard(Card $card)
  {
    array_push($this->cards, $card);
  }
  // ...
}

The following test can be used to ensure the object state is modified accordingly:

<?php
class CardCollectionTest extends PHPUnit_Framework_TestCase
{
  // ...
  public function testAddCardAffectAttribute()
  {
    $card = new Card('A', 'Spades');
    $this->cardCollection->addCard($card);
    $this->assertAttributeEquals(array($card), 'cards', $this->cardCollection);
  }
  // ...
}

How it works...

This test shows how you can inspect the private or protected state of a given object. PHPUnit has a series of attribute assertions that you can use to test the value of any attribute on a class even if it has protected or private visibility. Whenever possible you should use the public interface of an object to test this; however, in the event that it is not possible, the attribute assertions can come in very handy. The assertAttributeEquals() method is similar to its non-attribute counterpart assertEquals(). However, instead of passing the value you are testing, you pass the name of the attribute you want to test as the second parameter and the object that attribute is set on as the third parameter. As always, the expected value is passed in as the first parameter.

PHPUnit contains attribute equivalents for the standard set of assertions. You can compare values, check contents of arrays, compare array counts, and so on. Anything you would typically do with a variable in a unit test can also be accomplished in attributes using the attribute assertions.

Private and protected methods

PHPUnit doesn't provide the same functionality above for private and protected methods. However, if you are using PHP 5.3.2 or higher you can use reflection to alter the visibility of the method you are trying to test.

In CliFormatter there is a private method, getCard(), that is used to format a given card into a readable string.

<?php
class CliFormatter
{
  // ...
  private function getCard(Card $card)
  {
    return $card->getNumber() . substr($card->getSuit(), 0, 1);
  }
  // ...
}

Using reflection we can expose this method and invoke it as a part of a test.

<?php
class CliFormatterTest extends PHPUnit_Framework_TestCase
{
  // ...
  public function testGetCard()
  {
    $method = new ReflectionMethod('CliFormatter', 'getCard');
    $method->setAccessible(true);

    $card = new Card('A', 'Spades');
    $this->assertEquals('AS', $method->invoke($this->formatter, $card));
  }
  // ...
}

The ReflectionMethod::setAccessible() method can be used to allow a method to be invoked. However, you must invoke that method using the ReflectionMethod::invoke() method. If we attempted to call $this|formatter|getCard() directly then it would fail. This does keep us from having to clean up the accessibility. Your client code will continue to work as you originally wrote it. You don't have to worry about the method continuing to be accessible.

arrow left Previous Chapter
You have been reading a chapter from
Instant Hands-on Testing with PHPUnit How-to
Published in: May 2013 Publisher: Packt ISBN-13: 9781782169581
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime}