3 namespace Drupal\Tests;
5 use Drupal\Component\FileCache\FileCacheFactory;
6 use Drupal\Component\Utility\NestedArray;
7 use Drupal\Component\Utility\Random;
8 use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
9 use Drupal\Core\DependencyInjection\ContainerBuilder;
10 use Drupal\Core\StringTranslation\TranslatableMarkup;
11 use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
12 use PHPUnit\Framework\TestCase;
15 * Provides a base class and helpers for Drupal unit tests.
19 abstract class UnitTestCase extends TestCase {
21 use PhpunitCompatibilityTrait;
24 * The random generator.
26 * @var \Drupal\Component\Utility\Random
28 protected $randomGenerator;
40 protected function setUp() {
42 // Ensure that an instantiated container in the global state of \Drupal from
43 // a previous test does not leak into this test.
44 \Drupal::unsetContainer();
46 // Ensure that the NullFileCache implementation is used for the FileCache as
47 // unit tests should not be relying on caches implicitly.
48 FileCacheFactory::setConfiguration([FileCacheFactory::DISABLE_CACHE => TRUE]);
49 // Ensure that FileCacheFactory has a prefix.
50 FileCacheFactory::setPrefix('prefix');
52 $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
56 * Generates a unique random string containing letters and numbers.
59 * Length of random string to generate.
62 * Randomly generated unique string.
64 * @see \Drupal\Component\Utility\Random::name()
66 public function randomMachineName($length = 8) {
67 return $this->getRandomGenerator()->name($length, TRUE);
71 * Gets the random generator for the utility methods.
73 * @return \Drupal\Component\Utility\Random
74 * The random generator
76 protected function getRandomGenerator() {
77 if (!is_object($this->randomGenerator)) {
78 $this->randomGenerator = new Random();
80 return $this->randomGenerator;
84 * Asserts if two arrays are equal by sorting them first.
86 * @param array $expected
87 * @param array $actual
88 * @param string $message
90 protected function assertArrayEquals(array $expected, array $actual, $message = NULL) {
93 $this->assertEquals($expected, $actual, $message);
97 * Returns a stub config factory that behaves according to the passed array.
99 * Use this to generate a config factory that will return the desired values
100 * for the given config names.
102 * @param array $configs
103 * An associative array of configuration settings whose keys are
104 * configuration object names and whose values are key => value arrays for
105 * the configuration object in question. Defaults to an empty array.
107 * @return \PHPUnit_Framework_MockObject_MockBuilder
108 * A MockBuilder object for the ConfigFactory with the desired return
111 public function getConfigFactoryStub(array $configs = []) {
112 $config_get_map = [];
113 $config_editable_map = [];
114 // Construct the desired configuration object stubs, each with its own
115 // desired return map.
116 foreach ($configs as $config_name => $config_values) {
117 // Define a closure over the $config_values, which will be used as a
118 // returnCallback below. This function will mimic
119 // \Drupal\Core\Config\Config::get and allow using dotted keys.
120 $config_get = function ($key = '') use ($config_values) {
121 // Allow to pass in no argument.
123 return $config_values;
125 // See if we have the key as is.
126 if (isset($config_values[$key])) {
127 return $config_values[$key];
129 $parts = explode('.', $key);
130 $value = NestedArray::getValue($config_values, $parts, $key_exists);
131 return $key_exists ? $value : NULL;
134 $immutable_config_object = $this->getMockBuilder('Drupal\Core\Config\ImmutableConfig')
135 ->disableOriginalConstructor()
137 $immutable_config_object->expects($this->any())
139 ->will($this->returnCallback($config_get));
140 $config_get_map[] = [$config_name, $immutable_config_object];
142 $mutable_config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
143 ->disableOriginalConstructor()
145 $mutable_config_object->expects($this->any())
147 ->will($this->returnCallback($config_get));
148 $config_editable_map[] = [$config_name, $mutable_config_object];
150 // Construct a config factory with the array of configuration object stubs
151 // as its return map.
152 $config_factory = $this->createMock('Drupal\Core\Config\ConfigFactoryInterface');
153 $config_factory->expects($this->any())
155 ->will($this->returnValueMap($config_get_map));
156 $config_factory->expects($this->any())
157 ->method('getEditable')
158 ->will($this->returnValueMap($config_editable_map));
159 return $config_factory;
163 * Returns a stub config storage that returns the supplied configuration.
165 * @param array $configs
166 * An associative array of configuration settings whose keys are
167 * configuration object names and whose values are key => value arrays
168 * for the configuration object in question.
170 * @return \Drupal\Core\Config\StorageInterface
171 * A mocked config storage.
173 public function getConfigStorageStub(array $configs) {
174 $config_storage = $this->createMock('Drupal\Core\Config\NullStorage');
175 $config_storage->expects($this->any())
177 ->will($this->returnValue(array_keys($configs)));
179 foreach ($configs as $name => $config) {
180 $config_storage->expects($this->any())
182 ->with($this->equalTo($name))
183 ->will($this->returnValue($config));
185 return $config_storage;
189 * Mocks a block with a block plugin.
191 * @param string $machine_name
192 * The machine name of the block plugin.
194 * @return \Drupal\block\BlockInterface|\PHPUnit_Framework_MockObject_MockObject
197 * @deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Unit test
198 * base classes should not have dependencies on extensions. Set up mocks in
201 * @see https://www.drupal.org/node/2896072
203 protected function getBlockMockWithMachineName($machine_name) {
204 $plugin = $this->getMockBuilder('Drupal\Core\Block\BlockBase')
205 ->disableOriginalConstructor()
207 $plugin->expects($this->any())
208 ->method('getMachineNameSuggestion')
209 ->will($this->returnValue($machine_name));
211 $block = $this->getMockBuilder('Drupal\block\Entity\Block')
212 ->disableOriginalConstructor()
214 $block->expects($this->any())
215 ->method('getPlugin')
216 ->will($this->returnValue($plugin));
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);
222 * Returns a stub translation manager that just returns the passed string.
224 * @return \PHPUnit_Framework_MockObject_MockObject|\Drupal\Core\StringTranslation\TranslationInterface
225 * A mock translation object.
227 public function getStringTranslationStub() {
228 $translation = $this->createMock('Drupal\Core\StringTranslation\TranslationInterface');
229 $translation->expects($this->any())
230 ->method('translate')
231 ->willReturnCallback(function ($string, array $args = [], array $options = []) use ($translation) {
232 return new TranslatableMarkup($string, $args, $options, $translation);
234 $translation->expects($this->any())
235 ->method('translateString')
236 ->willReturnCallback(function (TranslatableMarkup $wrapper) {
237 return $wrapper->getUntranslatedString();
239 $translation->expects($this->any())
240 ->method('formatPlural')
241 ->willReturnCallback(function ($count, $singular, $plural, array $args = [], array $options = []) use ($translation) {
242 $wrapper = new PluralTranslatableMarkup($count, $singular, $plural, $args, $options, $translation);
249 * Sets up a container with a cache tags invalidator.
251 * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_validator
252 * The cache tags invalidator.
254 * @return \Symfony\Component\DependencyInjection\ContainerInterface|\PHPUnit_Framework_MockObject_MockObject
255 * The container with the cache tags invalidator service.
257 protected function getContainerWithCacheTagsInvalidator(CacheTagsInvalidatorInterface $cache_tags_validator) {
258 $container = $this->createMock('Symfony\Component\DependencyInjection\ContainerInterface');
259 $container->expects($this->any())
261 ->with('cache_tags.invalidator')
262 ->will($this->returnValue($cache_tags_validator));
264 \Drupal::setContainer($container);
269 * Returns a stub class resolver.
271 * @return \Drupal\Core\DependencyInjection\ClassResolverInterface|\PHPUnit_Framework_MockObject_MockObject
272 * The class resolver stub.
274 protected function getClassResolverStub() {
275 $class_resolver = $this->createMock('Drupal\Core\DependencyInjection\ClassResolverInterface');
276 $class_resolver->expects($this->any())
277 ->method('getInstanceFromDefinition')
278 ->will($this->returnCallback(function ($class) {
279 if (is_subclass_of($class, 'Drupal\Core\DependencyInjection\ContainerInjectionInterface')) {
280 return $class::create(new ContainerBuilder());
286 return $class_resolver;