summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2015-02-27 14:05:07 (GMT)
committerAlex Pott2015-02-27 14:05:07 (GMT)
commit9305a47f8012fd0da1d55a5a43f8dd813e24c78b (patch)
tree1a8d0baa8e7aa378ef66d5506ae4b5dde82506b2
parentdf0bde0a92194b1d2c16888f835b0eb423749cd9 (diff)
Issue #2144669 by Mile23, daffie, Nitesh Sethia: Improve/Refactor TestBase Through Expanded Unit Testing
-rw-r--r--core/modules/simpletest/src/TestBase.php28
-rw-r--r--core/modules/simpletest/tests/src/Unit/TestBaseTest.php465
2 files changed, 448 insertions, 45 deletions
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index 8fa831e..abf09fd 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -9,8 +9,8 @@ namespace Drupal\simpletest;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Random;
-use Drupal\Core\Database\Database;
use Drupal\Component\Utility\String;
+use Drupal\Core\Database\Database;
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Config\StorageComparer;
use Drupal\Core\DependencyInjection\ContainerBuilder;
@@ -344,6 +344,23 @@ abstract class TestBase {
}
/**
+ * Helper method to store an assertion record in the configured database.
+ *
+ * This method decouples database access from assertion logic.
+ *
+ * @param array $assertion
+ * Keyed array representing an assertion, as generated by assert().
+ *
+ * @see self::assert()
+ */
+ protected function storeAssertion(array $assertion) {
+ return self::getDatabaseConnection()
+ ->insert('simpletest')
+ ->fields($assertion)
+ ->execute();
+ }
+
+ /**
* Internal helper: stores the assert.
*
* @param $status
@@ -393,10 +410,7 @@ abstract class TestBase {
);
// Store assertion for display after the test has completed.
- self::getDatabaseConnection()
- ->insert('simpletest')
- ->fields($assertion)
- ->execute();
+ $this->storeAssertion($assertion);
// We do not use a ternary operator here to allow a breakpoint on
// test failure.
@@ -450,6 +464,7 @@ abstract class TestBase {
'file' => $caller['file'],
);
+ // We can't use storeAssertion() because this method is static.
return self::getDatabaseConnection()
->insert('simpletest')
->fields($assertion)
@@ -468,6 +483,7 @@ abstract class TestBase {
* @see \Drupal\simpletest\TestBase::insertAssert()
*/
public static function deleteAssert($message_id) {
+ // We can't use storeAssertion() because this method is static.
return (bool) self::getDatabaseConnection()
->delete('simpletest')
->condition('message_id', $message_id)
@@ -1334,7 +1350,6 @@ abstract class TestBase {
*/
public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
if ($severity & error_reporting()) {
- require_once DRUPAL_ROOT . '/core/includes/errors.inc';
$error_map = array(
E_STRICT => 'Run-time notice',
E_WARNING => 'Warning',
@@ -1369,7 +1384,6 @@ abstract class TestBase {
* @see set_exception_handler
*/
protected function exceptionHandler($exception) {
- require_once DRUPAL_ROOT . '/core/includes/errors.inc';
$backtrace = $exception->getTrace();
$verbose_backtrace = $backtrace;
// Push on top of the backtrace the call that generated the exception.
diff --git a/core/modules/simpletest/tests/src/Unit/TestBaseTest.php b/core/modules/simpletest/tests/src/Unit/TestBaseTest.php
index 9bac4fc..6fa9c55 100644
--- a/core/modules/simpletest/tests/src/Unit/TestBaseTest.php
+++ b/core/modules/simpletest/tests/src/Unit/TestBaseTest.php
@@ -2,7 +2,7 @@
/**
* @file
- * Contains \Drupal\simpletest\TestBaseTest.
+ * Contains \Drupal\Tests\simpletest\Unit\TestBaseTest.
*/
namespace Drupal\Tests\simpletest\Unit;
@@ -16,68 +16,457 @@ use Drupal\Tests\UnitTestCase;
class TestBaseTest extends UnitTestCase {
/**
- * A stub built using the TestBase class.
+ * Helper method for constructing a mock TestBase object.
*
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * TestBase is abstract, so we have to mock it. We'll also
+ * mock the storeAssertion() method so we don't need the database.
+ *
+ * @param string $test_id
+ * An identifying name for the mocked test.
+ *
+ * @return object
+ * Mock of Drupal\simpletest\TestBase.
*/
- protected $stub;
+ public function getTestBaseForAssertionTests($test_id) {
+ $mock_test_base = $this->getMockBuilder('Drupal\simpletest\TestBase')
+ ->setConstructorArgs(array($test_id))
+ ->setMethods(array('storeAssertion'))
+ ->getMockForAbstractClass();
+ // Override storeAssertion() so we don't need a database.
+ $mock_test_base->expects($this->any())
+ ->method('storeAssertion')
+ ->will($this->returnValue(NULL));
+ return $mock_test_base;
+ }
- protected function setUp() {
- $this->stub = $this->getMockForAbstractClass('Drupal\simpletest\TestBase');
+ /**
+ * Invoke methods that are protected or private.
+ *
+ * @param object $object
+ * Object on which to invoke the method.
+ * @param string $method_name
+ * Name of the method to invoke.
+ * @param array $arguments
+ * Array of arguments to be passed to the method.
+ *
+ * @return mixed
+ * Value returned by the invoked method.
+ */
+ public function invokeProtectedMethod($object, $method_name, array $arguments) {
+ $ref_method = new \ReflectionMethod($object, $method_name);
+ $ref_method->setAccessible(TRUE);
+ return $ref_method->invokeArgs($object, $arguments);
}
/**
* Provides data for the random string validation test.
*
* @return array
- * An array of values passed to the test method.
+ * - The expected result of the validation.
+ * - The string to validate.
*/
- public function randomStringValidateProvider () {
+ public function providerRandomStringValidate() {
return array(
- array(' curry paste', FALSE),
- array('curry paste ', FALSE),
- array('curry paste', FALSE),
- array('curry paste', FALSE),
- array('curry paste', TRUE),
- array('thai green curry paste', TRUE),
- array('@startswithat', FALSE),
- array('contains@at', TRUE),
+ array(FALSE, ' curry paste'),
+ array(FALSE, 'curry paste '),
+ array(FALSE, 'curry paste'),
+ array(FALSE, 'curry paste'),
+ array(TRUE, 'curry paste'),
+ array(TRUE, 'thai green curry paste'),
+ array(FALSE, '@startswithat'),
+ array(TRUE, 'contains@at'),
);
}
/**
- * Tests the random strings validation rules.
- *
- * @param string $string
- * The string to validate.
- * @param bool $expected
- * The expected result of the validation.
- *
- * @see \Drupal\simpletest\TestBase::randomStringValidate().
- *
- * @dataProvider randomStringValidateProvider
* @covers ::randomStringValidate
+ * @dataProvider providerRandomStringValidate
*/
- public function testRandomStringValidate($string, $expected) {
- $actual = $this->stub->randomStringValidate($string);
+ public function testRandomStringValidate($expected, $string) {
+ $mock_test_base = $this->getMockForAbstractClass('Drupal\simpletest\TestBase');
+ $actual = $mock_test_base->randomStringValidate($string);
$this->assertEquals($expected, $actual);
}
/**
- * Tests that the random string contains a non-alphanumeric character.
- *
- * @see \Drupal\simpletest\TestBase::randomString().
+ * Provides data for testRandomString() and others.
*
+ * @return array
+ * - The number of items (characters, object properties) we expect any of
+ * the random functions to give us.
+ */
+ public function providerRandomItems() {
+ return [
+ [NULL],
+ [0],
+ [1],
+ [2],
+ [3],
+ [4],
+ [7],
+ ];
+ }
+
+ /**
* @covers ::randomString
+ * @dataProvider providerRandomItems
+ */
+ public function testRandomString($length) {
+ $mock_test_base = $this->getMockForAbstractClass('Drupal\simpletest\TestBase');
+ $string = $mock_test_base->randomString($length);
+ $this->assertEquals($length, strlen($string));
+ // randomString() should always include an ampersand ('&') if $length is
+ // greater than 2.
+ if ($length > 2) {
+ $this->assertContains('&', $string);
+ }
+ }
+
+ /**
+ * @covers ::randomObject
+ * @dataProvider providerRandomItems
+ */
+ public function testRandomObject($size) {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ // Note: count((array)object) works for now, maybe not later.
+ $this->assertEquals($size, count((array) $test_base->randomObject($size)));
+ }
+
+ /**
+ * @covers ::checkRequirements
+ */
+ public function testCheckRequirements() {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertInternalType(
+ 'array',
+ $this->invokeProtectedMethod($test_base, 'checkRequirements', array())
+ );
+ }
+
+ /**
+ * Data provider for testAssert().
+ *
+ * @return array
+ * Standard dataProvider array of arrays:
+ * - Expected result from assert().
+ * - Expected status stored in TestBase->assertions.
+ * - Status, passed to assert().
+ * - Message, passed to assert().
+ * - Group, passed to assert().
+ * - Caller, passed to assert().
+ */
+ public function providerAssert() {
+ return array(
+ array(TRUE, 'pass', TRUE, 'Yay pass', 'test', array()),
+ array(FALSE, 'fail', FALSE, 'Boo fail', 'test', array()),
+ array(TRUE, 'pass', 'pass', 'Yay pass', 'test', array()),
+ array(FALSE, 'fail', 'fail', 'Boo fail', 'test', array()),
+ array(FALSE, 'exception', 'exception', 'Boo fail', 'test', array()),
+ array(FALSE, 'debug', 'debug', 'Boo fail', 'test', array()),
+ );
+ }
+
+ /**
+ * @covers ::assert
+ * @dataProvider providerAssert
+ */
+ public function testAssert($expected, $assertion_status, $status, $message, $group, $caller) {
+ $test_id = 'luke_i_am_your_' . $assertion_status;
+ $test_base = $this->getTestBaseForAssertionTests($test_id);
+
+ // Verify some startup values.
+ $this->assertAttributeEmpty('assertions', $test_base);
+ if (is_string($status)) {
+ $this->assertEquals(0, $test_base->results['#' . $status]);
+ }
+
+ // assert() is protected so we have to make it accessible.
+ $ref_assert = new \ReflectionMethod($test_base, 'assert');
+ $ref_assert->setAccessible(TRUE);
+
+ // Call assert() from within our hall of mirrors.
+ $this->assertEquals(
+ $expected,
+ $ref_assert->invokeArgs($test_base,
+ array($status, $message, $group, $caller)
+ )
+ );
+
+ // Check the side-effects of assert().
+ if (is_string($status)) {
+ $this->assertEquals(1, $test_base->results['#' . $status]);
+ }
+ $this->assertAttributeNotEmpty('assertions', $test_base);
+ // Make a ReflectionProperty for the assertions property,
+ // since it's protected.
+ $ref_assertions = new \ReflectionProperty($test_base, 'assertions');
+ $ref_assertions->setAccessible(TRUE);
+ $assertions = $ref_assertions->getValue($test_base);
+ $assertion = reset($assertions);
+ $this->assertEquals($assertion_status, $assertion['status']);
+ $this->assertEquals($test_id, $assertion['test_id']);
+ $this->assertEquals(get_class($test_base), $assertion['test_class']);
+ $this->assertEquals($message, $assertion['message']);
+ $this->assertEquals($group, $assertion['message_group']);
+ }
+
+ /**
+ * Data provider for assertTrue().
+ */
+ public function providerAssertTrue() {
+ return array(
+ array(TRUE, TRUE),
+ array(FALSE, FALSE),
+ );
+ }
+
+ /**
+ * @covers ::assertTrue
+ * @dataProvider providerAssertTrue
+ */
+ public function testAssertTrue($expected, $value) {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ $expected,
+ $this->invokeProtectedMethod($test_base, 'assertTrue', array($value))
+ );
+ }
+
+ /**
+ * @covers ::assertFalse
+ * @dataProvider providerAssertTrue
+ */
+ public function testAssertFalse($expected, $value) {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ (!$expected),
+ $this->invokeProtectedMethod($test_base, 'assertFalse', array($value))
+ );
+ }
+
+ /**
+ * Data provider for assertNull().
+ */
+ public function providerAssertNull() {
+ return array(
+ array(TRUE, NULL),
+ array(FALSE, ''),
+ );
+ }
+
+ /**
+ * @covers ::assertNull
+ * @dataProvider providerAssertNull
+ */
+ public function testAssertNull($expected, $value) {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ $expected,
+ $this->invokeProtectedMethod($test_base, 'assertNull', array($value))
+ );
+ }
+
+ /**
+ * @covers ::assertNotNull
+ * @dataProvider providerAssertNull
+ */
+ public function testAssertNotNull($expected, $value) {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ (!$expected),
+ $this->invokeProtectedMethod($test_base, 'assertNotNull', array($value))
+ );
+ }
+
+ /**
+ * Data provider for tests of equality assertions.
+ *
+ * Used by testAssertIdentical(), testAssertEqual(), testAssertNotIdentical(),
+ * and testAssertNotEqual().
+ *
+ * @return
+ * Array of test data.
+ * - Expected assertion value for identical comparison.
+ * - Expected assertion value for equal comparison.
+ * - First value to compare.
+ * - Second value to compare.
+ */
+ public function providerEqualityAssertions() {
+ return [
+ // Integers and floats.
+ [TRUE, TRUE, 0, 0],
+ [FALSE, TRUE, 0, 0.0],
+ [FALSE, TRUE, '0', 0],
+ [FALSE, TRUE, '0.0', 0.0],
+ [FALSE, FALSE, 23, 77],
+ [TRUE, TRUE, 23.0, 23.0],
+ // Strings.
+ [FALSE, FALSE, 'foof', 'yay'],
+ [TRUE, TRUE, 'yay', 'yay'],
+ // Bools with type conversion.
+ [TRUE, TRUE, TRUE, TRUE],
+ [TRUE, TRUE, FALSE, FALSE],
+ [FALSE, TRUE, NULL, FALSE],
+ [FALSE, TRUE, 'TRUE', TRUE],
+ [FALSE, FALSE, 'FALSE', FALSE],
+ [FALSE, TRUE, 0, FALSE],
+ [FALSE, TRUE, 1, TRUE],
+ [FALSE, TRUE, -1, TRUE],
+ [FALSE, TRUE, '1', TRUE],
+ [FALSE, TRUE, '1.3', TRUE],
+ // Null.
+ [FALSE, FALSE, 'NULL', NULL],
+ [TRUE, TRUE, NULL, NULL],
+ ];
+ }
+
+ /**
+ * @covers ::assertIdentical
+ * @dataProvider providerEqualityAssertions
+ */
+ public function testAssertIdentical($expected_identical, $expected_equal, $first, $second) {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ $expected_identical,
+ $this->invokeProtectedMethod($test_base, 'assertIdentical', array($first, $second))
+ );
+ }
+
+ /**
+ * @covers ::assertNotIdentical
+ * @dataProvider providerEqualityAssertions
+ */
+ public function testAssertNotIdentical($expected_identical, $expected_equal, $first, $second) {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ (!$expected_identical),
+ $this->invokeProtectedMethod($test_base, 'assertNotIdentical', array($first, $second))
+ );
+ }
+
+ /**
+ * @covers ::assertEqual
+ * @dataProvider providerEqualityAssertions
+ */
+ public function testAssertEqual($expected_identical, $expected_equal, $first, $second) {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ $expected_equal,
+ $this->invokeProtectedMethod($test_base, 'assertEqual', array($first, $second))
+ );
+ }
+
+ /**
+ * @covers ::assertNotEqual
+ * @dataProvider providerEqualityAssertions
+ */
+ public function testAssertNotEqual($expected_identical, $expected_equal, $first, $second) {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ (!$expected_equal),
+ $this->invokeProtectedMethod($test_base, 'assertNotEqual', array($first, $second))
+ );
+ }
+
+ /**
+ * Data provider for testAssertIdenticalObject().
+ */
+ public function providerAssertIdenticalObject() {
+ $obj1 = new \stdClass();
+ $obj1->foof = 'yay';
+ $obj2 = $obj1;
+ $obj3 = clone $obj1;
+ $obj4 = new \stdClass();
+ return array(
+ array(TRUE, $obj1, $obj2),
+ array(TRUE, $obj1, $obj3),
+ array(FALSE, $obj1, $obj4),
+ );
+ }
+
+ /**
+ * @covers ::assertIdenticalObject
+ * @dataProvider providerAssertIdenticalObject
+ */
+ public function testAssertIdenticalObject($expected, $first, $second) {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ $expected,
+ $this->invokeProtectedMethod($test_base, 'assertIdenticalObject', array($first, $second))
+ );
+ }
+
+ /**
+ * @covers ::pass
*/
- public function testRandomString() {
- $string = $this->stub->randomString(8);
- $this->assertEquals(8, strlen($string));
- $this->assertContains('&', $string);
+ public function testPass() {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ TRUE,
+ $this->invokeProtectedMethod($test_base, 'pass', array())
+ );
+ }
- // Ensure that we can generate random strings with a length of 1.
- $string = $this->stub->randomString(1);
- $this->assertEquals(1, strlen($string));
+ /**
+ * @covers ::fail
+ */
+ public function testFail() {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertEquals(
+ FALSE,
+ $this->invokeProtectedMethod($test_base, 'fail', array())
+ );
+ }
+
+ /**
+ * Data provider for testError().
+ *
+ * @return array
+ * - Expected status for assertion.
+ * - Group for use in assert().
+ */
+ public function providerError() {
+ return array(
+ array('debug', 'User notice'),
+ array('exception', 'Not User notice'),
+ );
+ }
+
+ /**
+ * @covers ::error
+ * @dataProvider providerError
+ */
+ public function testError($status, $group) {
+ // Mock up a TestBase object.
+ $mock_test_base = $this->getMockBuilder('Drupal\simpletest\TestBase')
+ ->setMethods(array('assert'))
+ ->getMockForAbstractClass();
+
+ // Set expectations for assert().
+ $mock_test_base->expects($this->once())
+ ->method('assert')
+ // The first argument to assert() should be the expected $status. This is
+ // the most important expectation of this test.
+ ->with($status)
+ // Arbitrary return value.
+ ->willReturn("$status:$group");
+
+ // Invoke error().
+ $this->assertEquals(
+ "$status:$group",
+ $this->invokeProtectedMethod($mock_test_base, 'error', array('msg', $group))
+ );
+ }
+
+ /**
+ * @covers ::getRandomGenerator
+ */
+ public function testGetRandomGenerator() {
+ $test_base = $this->getTestBaseForAssertionTests('test_id');
+ $this->assertInstanceOf(
+ 'Drupal\Component\Utility\Random',
+ $this->invokeProtectedMethod($test_base, 'getRandomGenerator', array())
+ );
}
}