You are here

class ProtectedPrivatesTest in Examples for Developers 8

Same name and namespace in other branches
  1. 3.x modules/phpunit_example/tests/src/Unit/ProtectedPrivatesTest.php \Drupal\Tests\phpunit_example\Unit\ProtectedPrivatesTest

ProtectedPrivates unit testing of restricted methods.

This test case demonstrates the following unit testing patterns and topics:

  • Using reflection to test private class methods.
  • Using subclassing to test protected class methods.

If you are reading this and don't understand the basics of unit testing, start reading AddClassTest instead.

This test class uses reflection and subclassing to work around method access problems. Since, by design, a private method is inaccessible, we have to use reflection to gain access to the method for our own purposes.

The getAccessibleMethod() method demonstrates a way to do this.

Once we've set the method to be accessible, we can use it as if it were public.

The same technique can be used for protected methods. However, there might be times when it makes more sense to subclass the class under test, and just make a public accessor method that way. So we demonstrate that here in testProtectedAdd().

@group phpunit_example @group examples

Hierarchy

Expanded class hierarchy of ProtectedPrivatesTest

Related topics

File

phpunit_example/tests/src/Unit/ProtectedPrivatesTest.php, line 41

Namespace

Drupal\Tests\phpunit_example\Unit
View source
class ProtectedPrivatesTest extends UnitTestCase {

  /**
   * Get an accessible method using reflection.
   */
  public function getAccessibleMethod($class_name, $method_name) {
    $class = new \ReflectionClass($class_name);
    $method = $class
      ->getMethod($method_name);
    $method
      ->setAccessible(TRUE);
    return $method;
  }

  /**
   * Good data provider.
   */
  public function addDataProvider() {
    return [
      [
        5,
        2,
        3,
      ],
    ];
  }

  /**
   * Test ProtectedPrivate::privateAdd().
   *
   * We want to test a private method on a class. This is problematic
   * because, by design, we don't have access to this method. However,
   * we do have a tool available to help us out with this problem:
   * We can override the accessibility of a method using reflection.
   *
   * @dataProvider addDataProvider
   */
  public function testPrivateAdd($expected, $a, $b) {

    // Get a reflected, accessible version of the privateAdd() method.
    $private_method = $this
      ->getAccessibleMethod('Drupal\\phpunit_example\\ProtectedPrivates', 'privateAdd');

    // Create a new ProtectedPrivates object.
    $pp = new ProtectedPrivates();

    // Use the reflection to invoke on the object.
    $sum = $private_method
      ->invokeArgs($pp, [
      $a,
      $b,
    ]);

    // Make an assertion.
    $this
      ->assertEquals($expected, $sum);
  }

  /**
   * Bad data provider.
   */
  public function addBadDataProvider() {
    return [
      [
        'string',
        [],
      ],
    ];
  }

  /**
   * Test ProtectedPrivate::privateAdd() with bad data.
   *
   * This is essentially the same test as testPrivateAdd(), but using
   * non-numeric data. This lets us test the exception-throwing ability
   * of this private method.
   *
   * @dataProvider addBadDataProvider
   */
  public function testPrivateAddBadData($a, $b) {

    // Get a reflected, accessible version of the privateAdd() method.
    $private_method = $this
      ->getAccessibleMethod('Drupal\\phpunit_example\\ProtectedPrivates', 'privateAdd');

    // Create a new ProtectedPrivates object.
    $pp = new ProtectedPrivates();

    // Use the reflection to invoke on the object.
    // This should throw an exception.
    $this
      ->expectException(\InvalidArgumentException::class);
    $private_method
      ->invokeArgs($pp, [
      $a,
      $b,
    ]);
  }

  /**
   * Test ProtectedPrivates::protectedAdd() using a stub class.
   *
   * We could use the same reflection technique to test protected
   * methods, just like we did with private ones.
   *
   * But sometimes it might make more sense to use a stub class
   * which will have access to the protected method. That's what
   * we'll demonstrate here.
   *
   * @dataProvider addDataProvider
   */
  public function testProtectedAdd($expected, $a, $b) {
    $stub = new ProtectedPrivatesSubclass();
    $this
      ->assertEquals($expected, $stub
      ->subclassProtectedAdd($a, $b));
  }

  /**
   * Test ProtectedPrivates::protectedAdd() with bad data using a stub class.
   *
   * This test is similar to testProtectedAdd(), but expects an exception.
   *
   * @dataProvider addBadDataProvider
   */
  public function testProtectedAddBadData($a, $b) {
    $stub = new ProtectedPrivatesSubclass();
    $this
      ->expectException(\InvalidArgumentException::class);
    $stub
      ->subclassProtectedAdd($a, $b);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
PhpunitCompatibilityTrait::getMock Deprecated public function Returns a mock object for the specified class using the available method.
PhpunitCompatibilityTrait::setExpectedException Deprecated public function Compatibility layer for PHPUnit 6 to support PHPUnit 4 code.
ProtectedPrivatesTest::addBadDataProvider public function Bad data provider.
ProtectedPrivatesTest::addDataProvider public function Good data provider.
ProtectedPrivatesTest::getAccessibleMethod public function Get an accessible method using reflection.
ProtectedPrivatesTest::testPrivateAdd public function Test ProtectedPrivate::privateAdd().
ProtectedPrivatesTest::testPrivateAddBadData public function Test ProtectedPrivate::privateAdd() with bad data.
ProtectedPrivatesTest::testProtectedAdd public function Test ProtectedPrivates::protectedAdd() using a stub class.
ProtectedPrivatesTest::testProtectedAddBadData public function Test ProtectedPrivates::protectedAdd() with bad data using a stub class.
UnitTestCase::$randomGenerator protected property The random generator.
UnitTestCase::$root protected property The app root. 1
UnitTestCase::assertArrayEquals protected function Asserts if two arrays are equal by sorting them first.
UnitTestCase::getBlockMockWithMachineName Deprecated protected function Mocks a block with a block plugin. 1
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getConfigStorageStub public function Returns a stub config storage that returns the supplied configuration.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getRandomGenerator protected function Gets the random generator for the utility methods.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::randomMachineName public function Generates a unique random string containing letters and numbers.
UnitTestCase::setUp protected function 340