Civi\Test\Invasive - Add helper for checking protected/private members
authorTim Otten <totten@civicrm.org>
Thu, 31 Dec 2020 08:43:39 +0000 (00:43 -0800)
committerTim Otten <totten@civicrm.org>
Thu, 31 Dec 2020 09:31:49 +0000 (01:31 -0800)
Civi/Test/Invasive.php [new file with mode: 0644]
tests/phpunit/Civi/Test/InvasiveExample.php [new file with mode: 0644]
tests/phpunit/Civi/Test/InvasiveTest.php [new file with mode: 0644]

diff --git a/Civi/Test/Invasive.php b/Civi/Test/Invasive.php
new file mode 100644 (file)
index 0000000..fe8f5ae
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+namespace Civi\Test;
+
+/**
+ * The "Invasive"  helper makes it a bit easier to write unit-tests which
+ * touch upon private or protected members.
+ *
+ * @package Civi\Test
+ */
+class Invasive {
+
+  /**
+   * Call a private/protected method.
+   *
+   * This is only intended for unit-testing.
+   *
+   * @param array $callable
+   *   Ex: [$myObject, 'myPrivateMethod']
+   *   Ex: ['MyClass', 'myPrivateStaticMethod']
+   * @param array $args
+   *   Ordered list of arguments.
+   * @return mixed
+   */
+  public static function call($callable, $args = []) {
+    list ($class, $object, $member) = self::parseRef($callable);
+    $reflection = new \ReflectionMethod($class, $member);
+    $reflection->setAccessible(TRUE);
+    return $reflection->invokeArgs($object, $args);
+  }
+
+  /**
+   * Get the content of a private/protected method.
+   *
+   * This is only intended for unit-testing.
+   *
+   * @param array $ref
+   *   A reference to class+property.
+   *   Ex: [$myObject, 'myPrivateField']
+   *   Ex: ['MyClass', 'myPrivateStaticField']
+   * @return mixed
+   */
+  public static function get($ref) {
+    list ($class, $object, $member) = self::parseRef($ref);
+    $reflection = new \ReflectionProperty($class, $member);
+    $reflection->setAccessible(TRUE);
+    return $reflection->getValue($object);
+  }
+
+  /**
+   * Get the content of a private/protected method.
+   *
+   * This is only intended for unit-testing.
+   *
+   * @param array $ref
+   *   A reference to class+property.
+   *   Ex: [$myObject, 'myPrivateField']
+   *   Ex: ['MyClass', 'myPrivateStaticField']
+   * @param mixed $value
+   * @return mixed
+   */
+  public static function set($ref, $value) {
+    list ($class, $object, $member) = self::parseRef($ref);
+    $reflection = new \ReflectionProperty($class, $member);
+    $reflection->setAccessible(TRUE);
+    $reflection->setValue($object, $value);
+  }
+
+  /**
+   * @param array $callable
+   *   Ex: [$myObject, 'myPrivateMember']
+   *   Ex: ['MyClass', 'myPrivateStaticMember']
+   * @return array
+   *   Ordered array of [string $class, object? $object, string $memberName].
+   */
+  private static function parseRef($callable) {
+    if (is_string($callable)) {
+      list ($class, $member) = explode('::', $callable);
+      return [$class, NULL, $member];
+    }
+    elseif (is_string($callable[0])) {
+      return [$callable[0], NULL, $callable[1]];
+    }
+    elseif (is_object($callable[0])) {
+      return [get_class($callable[0]), $callable[0], $callable[1]];
+    }
+    else {
+      throw new \RuntimeException("Cannot parse reference to private member");
+    }
+  }
+
+}
diff --git a/tests/phpunit/Civi/Test/InvasiveExample.php b/tests/phpunit/Civi/Test/InvasiveExample.php
new file mode 100644 (file)
index 0000000..15ffcea
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Test;
+
+/**
+ * Class InvasiveExample
+ *
+ * This is a dummy/placeholder for use with InvasiveTest.
+ *
+ * @package Civi\Test
+ */
+class InvasiveExample {
+
+  private $privateField = 10;
+
+  private static $protectedStaticField = 20;
+
+  /**
+   * @return int
+   */
+  private function getPrivateField(): int {
+    return $this->privateField;
+  }
+
+  /**
+   * @param int $privateField
+   */
+  private function setPrivateField(int $privateField) {
+    $this->privateField = $privateField;
+  }
+
+  private function twiddlePrivateField(&$output) {
+    $output = $this->privateField * 100;
+    return $this->privateField * 10000;
+  }
+
+  /**
+   * @return int
+   */
+  protected static function getProtectedStaticField(): int {
+    return self::$protectedStaticField;
+  }
+
+  /**
+   * @param int $protectedStaticField
+   */
+  protected static function setProtectedStaticField(int $protectedStaticField) {
+    self::$protectedStaticField = $protectedStaticField;
+  }
+
+}
diff --git a/tests/phpunit/Civi/Test/InvasiveTest.php b/tests/phpunit/Civi/Test/InvasiveTest.php
new file mode 100644 (file)
index 0000000..7b109e3
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Test;
+
+class InvasiveTest extends \CiviUnitTestCase {
+
+  public function testPrivate() {
+    $tgt = new InvasiveExample();
+    $this->assertEquals(10, Invasive::get([$tgt, 'privateField']));
+    $this->assertEquals(10, Invasive::call([$tgt, 'getPrivateField']));
+    Invasive::call([$tgt, 'setPrivateField'], [11]);
+    $this->assertEquals(11, Invasive::call([$tgt, 'getPrivateField']));
+
+    $theRef = NULL;
+    $this->assertEquals(110000, Invasive::call([$tgt, 'twiddlePrivateField'], [&$theRef]));
+    $this->assertEquals(1100, $theRef);
+  }
+
+  public function testProtectedStatic() {
+    $tgt = InvasiveExample::class;
+    $this->assertEquals(20, Invasive::get([$tgt, 'protectedStaticField']));
+    $this->assertEquals(20, Invasive::call([$tgt, 'getProtectedStaticField']));
+    Invasive::call([$tgt, 'setProtectedStaticField'], [21]);
+    $this->assertEquals(21, Invasive::call([$tgt, 'getProtectedStaticField']));
+  }
+
+}