On Github carlosreig / charla-phpunit
PHPUnit es un framework orientado a objetos para realizar testing en PHP.
Testing, en este caso, es automatizar el proceso de comprobar que las cosas funcionan como hemos pensado que lo hagan.
También hay testing manual..../core/tests/Drupal/Tests/
./core/modules/%MODULE%/tests/src/
php ./core/vendor/bin/phpunit --configuration ./core
drush test-run
admin/config/development/testing/
public function testSiEstaClaro() { $this->assertTrue( true ); }
Básicamente, un test se divide en tres fases...
Si el método es público pero no empieza por test, se puede indicar a PHPUnit que debe ejecutarlo añadiendo la anotación @test al PHPDoc Normalmente el nombre del test case (el de la clase) es el nombre de la clase que está testeando + Test$this->config = array( 'one' => '1', 'two' => '2', ); $this->settings = new Settings($this->config); ... $this->assertEquals($this->config['one'], Settings::get('one')); $this->assertEquals($this->config['two'], Settings::get('two')); Drupal\Tests\Core\Site\SettingsTest::testGet
//Preparar $this->config = array( 'one' => '1', 'two' => '2', ); //Actuar $this->settings = new Settings($this->config); $settingOne = Settings::get('one'); $settingTwo = Settings::get('two'); ... //Comprobar $this->assertEquals($this->config['one'], $settingOne); $this->assertEquals($this->config['two'], $settingTwo); Drupal\Tests\Core\Site\SettingsTest::testGet
Cuando escribes un test necesitas empezar en un estado conocido. Este estado es la fixture del test.
Se suele hacer en el setUp del test case
Ejemplo de la pilaHacen fallar el test si no se cumplen.
Hay 36 diferentes: assertArrayHasKey, assertClassHasAttribute, assertClassHasStaticAttribute, assertContains, assertContainsOnly, assertContainsOnlyInstancesOf, assertCount, assertEmpty, assertEqualXMLStructure, assertEquals, assertFalse, assertFileEquals, assertFileExists, assertGreaterThan, assertGreaterThanOrEqual, assertInstanceOf, assertInternalType, assertJsonFileEqualsJsonFile, assertJsonStringEqualsJsonFile, assertJsonStringEqualsJsonString, assertLessThan, assertLessThanOrEqual, assertNull, assertObjectHasAttribute, assertRegExp, assertStringMatchesFormat, assertStringMatchesFormatFile, assertSame, assertStringEndsWith, assertStringEqualsFile, assertStringStartsWith, assertThat, assertTrue, assertXmlFileEqualsXmlFile, assertXmlStringEqualsXmlFile, assertXmlStringEqualsXmlString.
class TimerTest extends UnitTestCase { /** * Tests Timer::read() time accumulation accuracy across multiple restarts. * * @see \Drupal\Component\Utility\Timer::read() */ public function testTimer() { Timer::start('test'); usleep(5000); $value = Timer::read('test'); usleep(5000); $value2 = Timer::read('test'); ... // Although we sleep for 5 milliseconds, we should test that at least 4 ms // have past because usleep() is not reliable on Windows. See // http://php.net/manual/en/function.usleep.php for more information. The // purpose of the test to validate that the Timer class can measure elapsed // time not the granularity of usleep() on a particular OS. $this->assertGreaterThanOrEqual(4, $value, 'Timer failed to measure at least 4 milliseconds of sleeping while running.'); $this->assertGreaterThanOrEqual($value + 4, $value2, 'Timer failed to measure at least 8 milliseconds of sleeping while running.'); // Stop the timer. $value5 = Timer::stop('test'); $this->assertGreaterThanOrEqual($value4, $value5['time'], 'Timer measured after stopping was not greater than last measurement.'); ... } }Drupal\Tests\Component\Utility\TimerTest
class JsonTest extends UnitTestCase { /** * A test string with the full ASCII table. * * @var string */ protected $string; /** * An array of unsafe html characters which has to be encoded. * * @var array */ protected $htmlUnsafe; /** * An array of unsafe html characters which are already escaped. * * @var array */ protected $htmlUnsafeEscaped; /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); // Setup a string with the full ASCII table. // @todo: Add tests for non-ASCII characters and Unicode. $this->string = ''; for ($i = 1; $i < 128; $i++) { $this->string .= chr($i); } // Characters that must be escaped. // We check for unescaped " separately. $this->htmlUnsafe = array('<', '>', '\'', '&'); // The following are the encoded forms of: < > ' & " $this->htmlUnsafeEscaped = array('\u003C', '\u003E', '\u0027', '\u0026', '\u0022'); } /** * Tests encoding length. */ public function testEncodingLength() { // Verify that JSON encoding produces a string with all of the characters. $json = Json::encode($this->string); $this->assertTrue(strlen($json) > strlen($this->string), 'A JSON encoded string is larger than the source string.'); } /** * Tests end and start of the encoded string. */ public function testEncodingStartEnd() { $json = Json::encode($this->string); // The first and last characters should be ", and no others. $this->assertTrue($json[0] == '"', 'A JSON encoded string begins with ".'); $this->assertTrue($json[strlen($json) - 1] == '"', 'A JSON encoded string ends with ".'); $this->assertTrue(substr_count($json, '"') == 2, 'A JSON encoded string contains exactly two ".'); } /** * Tests converting PHP variables to JSON strings and back. */ public function testReversibility() { $json = Json::encode($this->string); // Verify that encoding/decoding is reversible. $json_decoded = Json::decode($json); $this->assertSame($this->string, $json_decoded, 'Encoding a string to JSON and decoding back results in the original string.'); } /** * Test the reversibility of structured data */ public function testStructuredReversibility() { // Verify reversibility for structured data. Also verify that necessary // characters are escaped. $source = array(TRUE, FALSE, 0, 1, '0', '1', $this->string, array('key1' => $this->string, 'key2' => array('nested' => TRUE))); $json = Json::encode($source); foreach ($this->htmlUnsafe as $char) { $this->assertTrue(strpos($json, $char) === FALSE, sprintf('A JSON encoded string does not contain %s.', $char)); } // Verify that JSON encoding escapes the HTML unsafe characters foreach ($this->htmlUnsafeEscaped as $char) { $this->assertTrue(strpos($json, $char) > 0, sprintf('A JSON encoded string contains %s.', $char)); } $json_decoded = Json::decode($json); $this->assertNotSame($source, $json, 'An array encoded in JSON is identical to the source.'); $this->assertSame($source, $json_decoded, 'Encoding structured data to JSON and decoding back not results in the original data.'); }Drupal\Tests\Component\Serialization\JsonTest
class ConfigEntityBaseUnitTest extends UnitTestCase { ... /** * @covers ::setStatus * @covers ::status */ public function testSetStatus() { $this->assertTrue($this->entity->status()); $this->assertSame($this->entity, $this->entity->setStatus(FALSE)); $this->assertFalse($this->entity->status()); $this->entity->setStatus(TRUE); $this->assertTrue($this->entity->status()); } /** * @covers ::enable * @depends testSetStatus */ public function testEnable() { $this->entity->setStatus(FALSE); $this->assertSame($this->entity, $this->entity->enable()); $this->assertTrue($this->entity->status()); } ... } \Drupal\Tests\Core\Config\Entity\ConfigEntityBaseUnitTestLos @depends se suelen usar para proveer de argumentos a otros tests. Si falla el test del cual dependen los demás, estos no se ejecutan.
class UuidTest extends UnitTestCase { /** * Dataprovider for UUID instance tests. * * @return array */ public function providerUuidInstances() { $instances = array(); $instances[][] = new Php(); // If valid PECL extensions exists add to list. if (function_exists('uuid_create') && !function_exists('uuid_make')) { $instances[][] = new Pecl(); } // If we are on Windows add the com implementation as well. if (function_exists('com_create_guid')) { $instances[][] = new Com(); } return $instances; } /** * Tests generating valid UUIDs. * * @dataProvider providerUuidInstances */ public function testGenerateUuid(UuidInterface $instance) { $this->assertTrue(Uuid::isValid($instance->generate()), sprintf('UUID generation for %s works.', get_class($instance))); } /** * Tests that generated UUIDs are unique. * * @dataProvider providerUuidInstances */ public function testUuidIsUnique(UuidInterface $instance) { $this->assertNotEquals($instance->generate(), $instance->generate(), sprintf('Same UUID was not generated twice with %s.', get_class($instance))); } /** * Tests UUID validation. * * @param string $uuid * The uuid to check against. * @param bool $is_valid * Whether the uuid is valid or not. * @param string $message * The message to display on failure. * * @dataProvider providerTestValidation */ public function testValidation($uuid, $is_valid, $message) { $this->assertSame($is_valid, Uuid::isValid($uuid), $message); } /** * Dataprovider for UUID instance tests. * * @return array * An array of arrays containing * - The Uuid to check against. * - (bool) Whether or not the Uuid is valid. * - Failure message. */ public function providerTestValidation() { return array( // These valid UUIDs. array('6ba7b810-9dad-11d1-80b4-00c04fd430c8', TRUE, 'Basic FQDN UUID did not validate'), array('00000000-0000-0000-0000-000000000000', TRUE, 'Minimum UUID did not validate'), array('ffffffff-ffff-ffff-ffff-ffffffffffff', TRUE, 'Maximum UUID did not validate'), // These are invalid UUIDs. array('0ab26e6b-f074-4e44-9da-601205fa0e976', FALSE, 'Invalid format was validated'), array('0ab26e6b-f074-4e44-9daf-1205fa0e9761f', FALSE, 'Invalid length was validated'), ); } } \Drupal\Tests\Component\Uuid\UuidTestCon los @dataProviders puedes ejecutar varias veces un test con distintos argumentos.
class DateTimePlusTest extends UnitTestCase { ... public function providerTestInvalidDates() { return array( // Test for invalid month names when we are using a short version // of the month. array('23 abc 2012', NULL, 'd M Y', "23 abc 2012 contains an invalid month name and did not produce errors."), // Test for invalid hour. array('0000-00-00T45:30:00', NULL, 'Y-m-d\TH:i:s', "0000-00-00T45:30:00 contains an invalid hour and did not produce errors."), // Test for invalid day. array('0000-00-99T05:30:00', NULL, 'Y-m-d\TH:i:s', "0000-00-99T05:30:00 contains an invalid day and did not produce errors."), // Test for invalid month. array('0000-75-00T15:30:00', NULL, 'Y-m-d\TH:i:s', "0000-75-00T15:30:00 contains an invalid month and did not produce errors."), // Test for invalid year. array('11-08-01T15:30:00', NULL, 'Y-m-d\TH:i:s', "11-08-01T15:30:00 contains an invalid year and did not produce errors."), ); } /** * @dataProvider providerTestInvalidDates * @expectedException \Exception */ public function testInvalidDates($input, $timezone, $format, $message) { DateTimePlus::createFromFormat($format, $input, $timezone); } ... \Drupal\Tests\Component\Datetime\DateTimePlusTestTambién se puede comprobar la salida del script o los errores de PHP.
Un mock es un objeto de mentira.
//Creamos el mock $oMock = $this->getMock('Clase_a_la_que_falsear'); //Cambiamos su comportamiento o hacemos comprobaciones $oMock->expects( número_de_llamadas_esperadas )1 ->method( 'metodo_que_vigilar' ) ->will( valor_a_devolver );
class AliasManagerTest extends UnitTestCase { protected $aliasManager; protected $aliasStorage; protected $aliasWhitelist; protected $languageManager; protected $cache; protected $cacheKey = 'preload-paths:key'; protected $path = 'key'; /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); $this->aliasStorage = $this->getMock('Drupal\Core\Path\AliasStorageInterface'); $this->aliasWhitelist = $this->getMock('Drupal\Core\Path\AliasWhitelistInterface'); $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); $this->aliasManager = new AliasManager($this->aliasStorage, $this->aliasWhitelist, $this->languageManager, $this->cache); } /** * Tests the getPathByAlias method for an alias that have no matching path. * * @covers ::getPathByAlias() */ public function testGetPathByAliasNoMatch() { $alias = $this->randomMachineName(); $language = new Language(array('id' => 'en')); $this->languageManager->expects($this->any()) ->method('getCurrentLanguage') ->with(LanguageInterface::TYPE_URL) ->will($this->returnValue($language)); $this->aliasStorage->expects($this->once()) ->method('lookupPathSource') ->with($alias, $language->getId()) ->will($this->returnValue(NULL)); $this->assertEquals($alias, $this->aliasManager->getPathByAlias($alias)); // Call it twice to test the static cache. $this->assertEquals($alias, $this->aliasManager->getPathByAlias($alias)); } /** * Tests the getPathByAlias method for an alias that have a matching path. * * @covers ::getPathByAlias() */ public function testGetPathByAliasNatch() { $alias = $this->randomMachineName(); $path = $this->randomMachineName(); $language = $this->setUpCurrentLanguage(); $this->aliasStorage->expects($this->once()) ->method('lookupPathSource') ->with($alias, $language->getId()) ->will($this->returnValue($path)); $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias)); // Call it twice to test the static cache. $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias)); } ...\Drupal\Tests\Core\Path\AliasManagerTest
class PasswordHashingTest extends UnitTestCase { protected $user; protected $password; protected $md5Password; ... protected function setUp() { parent::setUp(); $this->user = $this->getMockBuilder('Drupal\user\Entity\User') ->disableOriginalConstructor() ->getMock(); $this->passwordHasher = new PhpassHashedPassword(1); } ... public function testPasswordNeedsUpdate() { $this->user->expects($this->any()) ->method('getPassword') ->will($this->returnValue($this->md5Password)); // The md5 password should be flagged as needing an update. $this->assertTrue($this->passwordHasher->userNeedsNewHash($this->user), 'User with md5 password needs a new hash.'); } ... \Drupal\Tests\Core\Password\PasswordHashingTest
<!--?xml version="1.0" encoding="UTF-8"?--> <phpunit bootstrap="tests/bootstrap.php" colors="true"> <php> <!-- Set error reporting to E_ALL. --> <ini name="error_reporting" value="32767"> <!-- Do not limit the amount of memory tests take to run. --> <ini name="memory_limit" value="-1"> </ini></ini></php> <testsuites> <testsuite name="Drupal Unit Test Suite"> <directory>./tests</directory> <directory>./modules/*/tests</directory> <directory>../modules</directory> <directory>../sites/*/modules</directory> <!-- Exclude Composer's vendor directory so we don't run tests there. --> <exclude>./vendor</exclude> <!-- Exclude Drush tests. --> <exclude>./drush/tests</exclude> <!-- Exclude special-case files from config's test modules. --> <exclude>./modules/config/tests/config_test/src</exclude> </testsuite> </testsuites> <!-- Filter for coverage reports. --> <filter> <whitelist> <directory>./includes</directory> <directory>./lib</directory> <directory>./modules</directory> <directory>../modules</directory> <directory>../sites</directory> </whitelist> </filter> </phpunit>./core/phpunit.xml.dist