annotate core/tests/Drupal/Tests/UnitTestCase.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 1fec387a4317
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Tests;
Chris@0 4
Chris@0 5 use Drupal\Component\FileCache\FileCacheFactory;
Chris@14 6 use Drupal\Component\Utility\NestedArray;
Chris@0 7 use Drupal\Component\Utility\Random;
Chris@0 8 use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
Chris@0 9 use Drupal\Core\DependencyInjection\ContainerBuilder;
Chris@0 10 use Drupal\Core\StringTranslation\TranslatableMarkup;
Chris@0 11 use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
Chris@0 12 use PHPUnit\Framework\TestCase;
Chris@0 13
Chris@0 14 /**
Chris@0 15 * Provides a base class and helpers for Drupal unit tests.
Chris@0 16 *
Chris@0 17 * @ingroup testing
Chris@0 18 */
Chris@0 19 abstract class UnitTestCase extends TestCase {
Chris@0 20
Chris@12 21 use PhpunitCompatibilityTrait;
Chris@12 22
Chris@0 23 /**
Chris@0 24 * The random generator.
Chris@0 25 *
Chris@0 26 * @var \Drupal\Component\Utility\Random
Chris@0 27 */
Chris@0 28 protected $randomGenerator;
Chris@0 29
Chris@0 30 /**
Chris@0 31 * The app root.
Chris@0 32 *
Chris@0 33 * @var string
Chris@0 34 */
Chris@0 35 protected $root;
Chris@0 36
Chris@0 37 /**
Chris@0 38 * {@inheritdoc}
Chris@0 39 */
Chris@0 40 protected function setUp() {
Chris@0 41 parent::setUp();
Chris@0 42 // Ensure that an instantiated container in the global state of \Drupal from
Chris@0 43 // a previous test does not leak into this test.
Chris@0 44 \Drupal::unsetContainer();
Chris@0 45
Chris@0 46 // Ensure that the NullFileCache implementation is used for the FileCache as
Chris@0 47 // unit tests should not be relying on caches implicitly.
Chris@0 48 FileCacheFactory::setConfiguration([FileCacheFactory::DISABLE_CACHE => TRUE]);
Chris@0 49 // Ensure that FileCacheFactory has a prefix.
Chris@0 50 FileCacheFactory::setPrefix('prefix');
Chris@0 51
Chris@0 52 $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
Chris@0 53 }
Chris@0 54
Chris@0 55 /**
Chris@0 56 * Generates a unique random string containing letters and numbers.
Chris@0 57 *
Chris@0 58 * @param int $length
Chris@0 59 * Length of random string to generate.
Chris@0 60 *
Chris@0 61 * @return string
Chris@0 62 * Randomly generated unique string.
Chris@0 63 *
Chris@0 64 * @see \Drupal\Component\Utility\Random::name()
Chris@0 65 */
Chris@0 66 public function randomMachineName($length = 8) {
Chris@0 67 return $this->getRandomGenerator()->name($length, TRUE);
Chris@0 68 }
Chris@0 69
Chris@0 70 /**
Chris@0 71 * Gets the random generator for the utility methods.
Chris@0 72 *
Chris@0 73 * @return \Drupal\Component\Utility\Random
Chris@0 74 * The random generator
Chris@0 75 */
Chris@0 76 protected function getRandomGenerator() {
Chris@0 77 if (!is_object($this->randomGenerator)) {
Chris@0 78 $this->randomGenerator = new Random();
Chris@0 79 }
Chris@0 80 return $this->randomGenerator;
Chris@0 81 }
Chris@0 82
Chris@0 83 /**
Chris@0 84 * Asserts if two arrays are equal by sorting them first.
Chris@0 85 *
Chris@0 86 * @param array $expected
Chris@0 87 * @param array $actual
Chris@0 88 * @param string $message
Chris@0 89 */
Chris@0 90 protected function assertArrayEquals(array $expected, array $actual, $message = NULL) {
Chris@0 91 ksort($expected);
Chris@0 92 ksort($actual);
Chris@0 93 $this->assertEquals($expected, $actual, $message);
Chris@0 94 }
Chris@0 95
Chris@0 96 /**
Chris@14 97 * Returns a stub config factory that behaves according to the passed array.
Chris@0 98 *
Chris@0 99 * Use this to generate a config factory that will return the desired values
Chris@0 100 * for the given config names.
Chris@0 101 *
Chris@0 102 * @param array $configs
Chris@14 103 * An associative array of configuration settings whose keys are
Chris@14 104 * configuration object names and whose values are key => value arrays for
Chris@14 105 * the configuration object in question. Defaults to an empty array.
Chris@0 106 *
Chris@0 107 * @return \PHPUnit_Framework_MockObject_MockBuilder
Chris@14 108 * A MockBuilder object for the ConfigFactory with the desired return
Chris@14 109 * values.
Chris@0 110 */
Chris@0 111 public function getConfigFactoryStub(array $configs = []) {
Chris@0 112 $config_get_map = [];
Chris@0 113 $config_editable_map = [];
Chris@0 114 // Construct the desired configuration object stubs, each with its own
Chris@0 115 // desired return map.
Chris@0 116 foreach ($configs as $config_name => $config_values) {
Chris@14 117 // Define a closure over the $config_values, which will be used as a
Chris@14 118 // returnCallback below. This function will mimic
Chris@14 119 // \Drupal\Core\Config\Config::get and allow using dotted keys.
Chris@14 120 $config_get = function ($key = '') use ($config_values) {
Chris@14 121 // Allow to pass in no argument.
Chris@14 122 if (empty($key)) {
Chris@14 123 return $config_values;
Chris@14 124 }
Chris@14 125 // See if we have the key as is.
Chris@14 126 if (isset($config_values[$key])) {
Chris@14 127 return $config_values[$key];
Chris@14 128 }
Chris@14 129 $parts = explode('.', $key);
Chris@14 130 $value = NestedArray::getValue($config_values, $parts, $key_exists);
Chris@14 131 return $key_exists ? $value : NULL;
Chris@14 132 };
Chris@0 133
Chris@0 134 $immutable_config_object = $this->getMockBuilder('Drupal\Core\Config\ImmutableConfig')
Chris@0 135 ->disableOriginalConstructor()
Chris@0 136 ->getMock();
Chris@0 137 $immutable_config_object->expects($this->any())
Chris@0 138 ->method('get')
Chris@14 139 ->will($this->returnCallback($config_get));
Chris@0 140 $config_get_map[] = [$config_name, $immutable_config_object];
Chris@0 141
Chris@0 142 $mutable_config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
Chris@0 143 ->disableOriginalConstructor()
Chris@0 144 ->getMock();
Chris@0 145 $mutable_config_object->expects($this->any())
Chris@0 146 ->method('get')
Chris@14 147 ->will($this->returnCallback($config_get));
Chris@0 148 $config_editable_map[] = [$config_name, $mutable_config_object];
Chris@0 149 }
Chris@0 150 // Construct a config factory with the array of configuration object stubs
Chris@0 151 // as its return map.
Chris@12 152 $config_factory = $this->createMock('Drupal\Core\Config\ConfigFactoryInterface');
Chris@0 153 $config_factory->expects($this->any())
Chris@0 154 ->method('get')
Chris@0 155 ->will($this->returnValueMap($config_get_map));
Chris@0 156 $config_factory->expects($this->any())
Chris@0 157 ->method('getEditable')
Chris@0 158 ->will($this->returnValueMap($config_editable_map));
Chris@0 159 return $config_factory;
Chris@0 160 }
Chris@0 161
Chris@0 162 /**
Chris@0 163 * Returns a stub config storage that returns the supplied configuration.
Chris@0 164 *
Chris@0 165 * @param array $configs
Chris@0 166 * An associative array of configuration settings whose keys are
Chris@0 167 * configuration object names and whose values are key => value arrays
Chris@0 168 * for the configuration object in question.
Chris@0 169 *
Chris@0 170 * @return \Drupal\Core\Config\StorageInterface
Chris@0 171 * A mocked config storage.
Chris@0 172 */
Chris@0 173 public function getConfigStorageStub(array $configs) {
Chris@12 174 $config_storage = $this->createMock('Drupal\Core\Config\NullStorage');
Chris@0 175 $config_storage->expects($this->any())
Chris@0 176 ->method('listAll')
Chris@0 177 ->will($this->returnValue(array_keys($configs)));
Chris@0 178
Chris@0 179 foreach ($configs as $name => $config) {
Chris@0 180 $config_storage->expects($this->any())
Chris@0 181 ->method('read')
Chris@0 182 ->with($this->equalTo($name))
Chris@0 183 ->will($this->returnValue($config));
Chris@0 184 }
Chris@0 185 return $config_storage;
Chris@0 186 }
Chris@0 187
Chris@0 188 /**
Chris@0 189 * Mocks a block with a block plugin.
Chris@0 190 *
Chris@0 191 * @param string $machine_name
Chris@0 192 * The machine name of the block plugin.
Chris@0 193 *
Chris@0 194 * @return \Drupal\block\BlockInterface|\PHPUnit_Framework_MockObject_MockObject
Chris@0 195 * The mocked block.
Chris@14 196 *
Chris@14 197 * @deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Unit test
Chris@14 198 * base classes should not have dependencies on extensions. Set up mocks in
Chris@14 199 * individual tests.
Chris@14 200 *
Chris@14 201 * @see https://www.drupal.org/node/2896072
Chris@0 202 */
Chris@0 203 protected function getBlockMockWithMachineName($machine_name) {
Chris@0 204 $plugin = $this->getMockBuilder('Drupal\Core\Block\BlockBase')
Chris@0 205 ->disableOriginalConstructor()
Chris@0 206 ->getMock();
Chris@0 207 $plugin->expects($this->any())
Chris@0 208 ->method('getMachineNameSuggestion')
Chris@0 209 ->will($this->returnValue($machine_name));
Chris@0 210
Chris@0 211 $block = $this->getMockBuilder('Drupal\block\Entity\Block')
Chris@0 212 ->disableOriginalConstructor()
Chris@0 213 ->getMock();
Chris@0 214 $block->expects($this->any())
Chris@0 215 ->method('getPlugin')
Chris@0 216 ->will($this->returnValue($plugin));
Chris@14 217 @trigger_error(__METHOD__ . ' is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Unit test base classes should not have dependencies on extensions. Set up mocks in individual tests.', E_USER_DEPRECATED);
Chris@0 218 return $block;
Chris@0 219 }
Chris@0 220
Chris@0 221 /**
Chris@0 222 * Returns a stub translation manager that just returns the passed string.
Chris@0 223 *
Chris@0 224 * @return \PHPUnit_Framework_MockObject_MockObject|\Drupal\Core\StringTranslation\TranslationInterface
Chris@0 225 * A mock translation object.
Chris@0 226 */
Chris@0 227 public function getStringTranslationStub() {
Chris@12 228 $translation = $this->createMock('Drupal\Core\StringTranslation\TranslationInterface');
Chris@0 229 $translation->expects($this->any())
Chris@0 230 ->method('translate')
Chris@0 231 ->willReturnCallback(function ($string, array $args = [], array $options = []) use ($translation) {
Chris@0 232 return new TranslatableMarkup($string, $args, $options, $translation);
Chris@0 233 });
Chris@0 234 $translation->expects($this->any())
Chris@0 235 ->method('translateString')
Chris@0 236 ->willReturnCallback(function (TranslatableMarkup $wrapper) {
Chris@0 237 return $wrapper->getUntranslatedString();
Chris@0 238 });
Chris@0 239 $translation->expects($this->any())
Chris@0 240 ->method('formatPlural')
Chris@0 241 ->willReturnCallback(function ($count, $singular, $plural, array $args = [], array $options = []) use ($translation) {
Chris@0 242 $wrapper = new PluralTranslatableMarkup($count, $singular, $plural, $args, $options, $translation);
Chris@0 243 return $wrapper;
Chris@0 244 });
Chris@0 245 return $translation;
Chris@0 246 }
Chris@0 247
Chris@0 248 /**
Chris@0 249 * Sets up a container with a cache tags invalidator.
Chris@0 250 *
Chris@0 251 * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_validator
Chris@0 252 * The cache tags invalidator.
Chris@0 253 *
Chris@0 254 * @return \Symfony\Component\DependencyInjection\ContainerInterface|\PHPUnit_Framework_MockObject_MockObject
Chris@0 255 * The container with the cache tags invalidator service.
Chris@0 256 */
Chris@0 257 protected function getContainerWithCacheTagsInvalidator(CacheTagsInvalidatorInterface $cache_tags_validator) {
Chris@12 258 $container = $this->createMock('Symfony\Component\DependencyInjection\ContainerInterface');
Chris@0 259 $container->expects($this->any())
Chris@0 260 ->method('get')
Chris@0 261 ->with('cache_tags.invalidator')
Chris@0 262 ->will($this->returnValue($cache_tags_validator));
Chris@0 263
Chris@0 264 \Drupal::setContainer($container);
Chris@0 265 return $container;
Chris@0 266 }
Chris@0 267
Chris@0 268 /**
Chris@0 269 * Returns a stub class resolver.
Chris@0 270 *
Chris@0 271 * @return \Drupal\Core\DependencyInjection\ClassResolverInterface|\PHPUnit_Framework_MockObject_MockObject
Chris@0 272 * The class resolver stub.
Chris@0 273 */
Chris@0 274 protected function getClassResolverStub() {
Chris@12 275 $class_resolver = $this->createMock('Drupal\Core\DependencyInjection\ClassResolverInterface');
Chris@0 276 $class_resolver->expects($this->any())
Chris@0 277 ->method('getInstanceFromDefinition')
Chris@0 278 ->will($this->returnCallback(function ($class) {
Chris@0 279 if (is_subclass_of($class, 'Drupal\Core\DependencyInjection\ContainerInjectionInterface')) {
Chris@0 280 return $class::create(new ContainerBuilder());
Chris@0 281 }
Chris@0 282 else {
Chris@0 283 return new $class();
Chris@0 284 }
Chris@0 285 }));
Chris@0 286 return $class_resolver;
Chris@0 287 }
Chris@0 288
Chris@0 289 }