3 namespace Drupal\Tests\Core\Entity;
5 use Drupal\Core\Access\AccessResult;
6 use Drupal\Core\Cache\Cache;
7 use Drupal\Core\DependencyInjection\ContainerBuilder;
8 use Drupal\Core\Language\Language;
9 use Drupal\Tests\UnitTestCase;
12 * @coversDefaultClass \Drupal\Core\Entity\Entity
16 class EntityUnitTest extends UnitTestCase {
19 * The entity under test.
21 * @var \Drupal\Core\Entity\Entity|\PHPUnit_Framework_MockObject_MockObject
26 * The entity type used for testing.
28 * @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
30 protected $entityType;
33 * The entity manager used for testing.
35 * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
37 protected $entityManager;
40 * The ID of the type of the entity under test.
44 protected $entityTypeId;
47 * The route provider used for testing.
49 * @var \Drupal\Core\Routing\RouteProvider|\PHPUnit_Framework_MockObject_MockObject
51 protected $routeProvider;
54 * The UUID generator used for testing.
56 * @var \Drupal\Component\Uuid\UuidInterface|\PHPUnit_Framework_MockObject_MockObject
61 * The language manager.
63 * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
65 protected $languageManager;
68 * The mocked cache tags invalidator.
70 * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
72 protected $cacheTagsInvalidator;
84 protected function setUp() {
88 'uuid' => '3bb9ee60-bea5-4622-b89b-a63319d10b3a',
90 $this->entityTypeId = $this->randomMachineName();
92 $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
93 $this->entityType->expects($this->any())
94 ->method('getListCacheTags')
95 ->willReturn([$this->entityTypeId . '_list']);
97 $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
98 $this->entityManager->expects($this->any())
99 ->method('getDefinition')
100 ->with($this->entityTypeId)
101 ->will($this->returnValue($this->entityType));
103 $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface');
105 $this->languageManager = $this->getMock('\Drupal\Core\Language\LanguageManagerInterface');
106 $this->languageManager->expects($this->any())
107 ->method('getLanguage')
109 ->will($this->returnValue(new Language(['id' => 'en'])));
111 $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidator');
113 $container = new ContainerBuilder();
114 $container->set('entity.manager', $this->entityManager);
115 $container->set('uuid', $this->uuid);
116 $container->set('language_manager', $this->languageManager);
117 $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
118 \Drupal::setContainer($container);
120 $this->entity = $this->getMockForAbstractClass('\Drupal\Core\Entity\Entity', [$this->values, $this->entityTypeId]);
126 public function testId() {
127 $this->assertSame($this->values['id'], $this->entity->id());
133 public function testUuid() {
134 $this->assertSame($this->values['uuid'], $this->entity->uuid());
139 * @covers ::enforceIsNew
141 public function testIsNew() {
142 // We provided an ID, so the entity is not new.
143 $this->assertFalse($this->entity->isNew());
144 // Force it to be new.
145 $this->assertSame($this->entity, $this->entity->enforceIsNew());
146 $this->assertTrue($this->entity->isNew());
150 * @covers ::getEntityType
152 public function testGetEntityType() {
153 $this->assertSame($this->entityType, $this->entity->getEntityType());
159 public function testBundle() {
160 $this->assertSame($this->entityTypeId, $this->entity->bundle());
166 public function testLabel() {
167 // Make a mock with one method that we use as the entity's uri_callback. We
168 // check that it is called, and that the entity's label is the callback's
170 $callback_label = $this->randomMachineName();
171 $property_label = $this->randomMachineName();
172 $callback_container = $this->getMock(get_class());
173 $callback_container->expects($this->once())
174 ->method(__FUNCTION__)
175 ->will($this->returnValue($callback_label));
176 $this->entityType->expects($this->at(0))
177 ->method('getLabelCallback')
178 ->will($this->returnValue([$callback_container, __FUNCTION__]));
179 $this->entityType->expects($this->at(1))
180 ->method('getLabelCallback')
181 ->will($this->returnValue(NULL));
182 $this->entityType->expects($this->at(2))
185 ->will($this->returnValue('label'));
187 // Set a dummy property on the entity under test to test that the label can
188 // be returned form a property if there is no callback.
189 $this->entityManager->expects($this->at(1))
190 ->method('getDefinition')
191 ->with($this->entityTypeId)
192 ->will($this->returnValue([
197 $this->entity->label = $property_label;
199 $this->assertSame($callback_label, $this->entity->label());
200 $this->assertSame($property_label, $this->entity->label());
206 public function testAccess() {
207 $access = $this->getMock('\Drupal\Core\Entity\EntityAccessControlHandlerInterface');
208 $operation = $this->randomMachineName();
209 $access->expects($this->at(0))
211 ->with($this->entity, $operation)
212 ->will($this->returnValue(AccessResult::allowed()));
213 $access->expects($this->at(1))
214 ->method('createAccess')
215 ->will($this->returnValue(AccessResult::allowed()));
216 $this->entityManager->expects($this->exactly(2))
217 ->method('getAccessControlHandler')
218 ->will($this->returnValue($access));
219 $this->assertEquals(AccessResult::allowed(), $this->entity->access($operation));
220 $this->assertEquals(AccessResult::allowed(), $this->entity->access('create'));
226 public function testLanguage() {
227 $this->entityType->expects($this->any())
229 ->will($this->returnValueMap([
230 ['langcode', 'langcode'],
232 $this->assertSame('en', $this->entity->language()->getId());
236 * Setup for the tests of the ::load() method.
238 public function setupTestLoad() {
239 // Base our mocked entity on a real entity class so we can test if calling
240 // Entity::load() on the base class will bubble up to an actual entity.
241 $this->entityTypeId = 'entity_test_mul';
242 $methods = get_class_methods('Drupal\entity_test\Entity\EntityTestMul');
243 unset($methods[array_search('load', $methods)]);
244 unset($methods[array_search('loadMultiple', $methods)]);
245 unset($methods[array_search('create', $methods)]);
246 $this->entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTestMul')
247 ->disableOriginalConstructor()
248 ->setMethods($methods)
256 * Tests Entity::load() when called statically on a subclass of Entity.
258 public function testLoad() {
259 $this->setupTestLoad();
261 $class_name = get_class($this->entity);
263 $this->entityManager->expects($this->once())
264 ->method('getEntityTypeFromClass')
266 ->willReturn($this->entityTypeId);
268 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
269 $storage->expects($this->once())
272 ->will($this->returnValue($this->entity));
273 $this->entityManager->expects($this->once())
274 ->method('getStorage')
275 ->with($this->entityTypeId)
276 ->will($this->returnValue($storage));
278 // Call Entity::load statically and check that it returns the mock entity.
279 $this->assertSame($this->entity, $class_name::load(1));
283 * @covers ::loadMultiple
285 * Tests Entity::loadMultiple() when called statically on a subclass of
288 public function testLoadMultiple() {
289 $this->setupTestLoad();
291 $class_name = get_class($this->entity);
293 $this->entityManager->expects($this->once())
294 ->method('getEntityTypeFromClass')
296 ->willReturn($this->entityTypeId);
298 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
299 $storage->expects($this->once())
300 ->method('loadMultiple')
302 ->will($this->returnValue([1 => $this->entity]));
303 $this->entityManager->expects($this->once())
304 ->method('getStorage')
305 ->with($this->entityTypeId)
306 ->will($this->returnValue($storage));
308 // Call Entity::loadMultiple statically and check that it returns the mock
310 $this->assertSame([1 => $this->entity], $class_name::loadMultiple([1]));
316 public function testCreate() {
317 $this->setupTestLoad();
319 $class_name = get_class($this->entity);
320 $this->entityManager->expects($this->once())
321 ->method('getEntityTypeFromClass')
323 ->willReturn($this->entityTypeId);
325 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
326 $storage->expects($this->once())
329 ->will($this->returnValue($this->entity));
330 $this->entityManager->expects($this->once())
331 ->method('getStorage')
332 ->with($this->entityTypeId)
333 ->will($this->returnValue($storage));
335 // Call Entity::create() statically and check that it returns the mock
337 $this->assertSame($this->entity, $class_name::create([]));
343 public function testSave() {
344 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
345 $storage->expects($this->once())
347 ->with($this->entity);
348 $this->entityManager->expects($this->once())
349 ->method('getStorage')
350 ->with($this->entityTypeId)
351 ->will($this->returnValue($storage));
352 $this->entity->save();
358 public function testDelete() {
359 $this->entity->id = $this->randomMachineName();
360 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
361 // Testing the argument of the delete() method consumes too much memory.
362 $storage->expects($this->once())
364 $this->entityManager->expects($this->once())
365 ->method('getStorage')
366 ->with($this->entityTypeId)
367 ->will($this->returnValue($storage));
368 $this->entity->delete();
372 * @covers ::getEntityTypeId
374 public function testGetEntityTypeId() {
375 $this->assertSame($this->entityTypeId, $this->entity->getEntityTypeId());
381 public function testPreSave() {
382 // This method is internal, so check for errors on calling it only.
383 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
384 // Our mocked entity->preSave() returns NULL, so assert that.
385 $this->assertNull($this->entity->preSave($storage));
391 public function testPostSave() {
392 $this->cacheTagsInvalidator->expects($this->at(0))
393 ->method('invalidateTags')
395 $this->entityTypeId . '_list', // List cache tag.
397 $this->cacheTagsInvalidator->expects($this->at(1))
398 ->method('invalidateTags')
400 $this->entityTypeId . ':' . $this->values['id'], // Own cache tag.
401 $this->entityTypeId . '_list', // List cache tag.
404 // This method is internal, so check for errors on calling it only.
405 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
407 // A creation should trigger the invalidation of the "list" cache tag.
408 $this->entity->postSave($storage, FALSE);
409 // An update should trigger the invalidation of both the "list" and the
411 $this->entity->postSave($storage, TRUE);
415 * @covers ::preCreate
417 public function testPreCreate() {
418 // This method is internal, so check for errors on calling it only.
419 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
421 // Our mocked entity->preCreate() returns NULL, so assert that.
422 $this->assertNull($this->entity->preCreate($storage, $values));
426 * @covers ::postCreate
428 public function testPostCreate() {
429 // This method is internal, so check for errors on calling it only.
430 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
431 // Our mocked entity->postCreate() returns NULL, so assert that.
432 $this->assertNull($this->entity->postCreate($storage));
436 * @covers ::preDelete
438 public function testPreDelete() {
439 // This method is internal, so check for errors on calling it only.
440 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
441 // Our mocked entity->preDelete() returns NULL, so assert that.
442 $this->assertNull($this->entity->preDelete($storage, [$this->entity]));
446 * @covers ::postDelete
448 public function testPostDelete() {
449 $this->cacheTagsInvalidator->expects($this->once())
450 ->method('invalidateTags')
452 $this->entityTypeId . ':' . $this->values['id'],
453 $this->entityTypeId . '_list',
455 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
456 $storage->expects($this->once())
457 ->method('getEntityType')
458 ->willReturn($this->entityType);
460 $entities = [$this->values['id'] => $this->entity];
461 $this->entity->postDelete($storage, $entities);
467 public function testPostLoad() {
468 // This method is internal, so check for errors on calling it only.
469 $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
470 $entities = [$this->entity];
471 // Our mocked entity->postLoad() returns NULL, so assert that.
472 $this->assertNull($this->entity->postLoad($storage, $entities));
476 * @covers ::referencedEntities
478 public function testReferencedEntities() {
479 $this->assertSame([], $this->entity->referencedEntities());
483 * @covers ::getCacheTags
484 * @covers ::getCacheTagsToInvalidate
485 * @covers ::addCacheTags
487 public function testCacheTags() {
488 // Ensure that both methods return the same by default.
489 $this->assertEquals([$this->entityTypeId . ':' . 1], $this->entity->getCacheTags());
490 $this->assertEquals([$this->entityTypeId . ':' . 1], $this->entity->getCacheTagsToInvalidate());
492 // Add an additional cache tag and make sure only getCacheTags() returns
494 $this->entity->addCacheTags(['additional_cache_tag']);
496 // EntityTypeId is random so it can shift order. We need to duplicate the
497 // sort from \Drupal\Core\Cache\Cache::mergeTags().
498 $tags = ['additional_cache_tag', $this->entityTypeId . ':' . 1];
500 $this->assertEquals($tags, $this->entity->getCacheTags());
501 $this->assertEquals([$this->entityTypeId . ':' . 1], $this->entity->getCacheTagsToInvalidate());
505 * @covers ::getCacheContexts
506 * @covers ::addCacheContexts
508 public function testCacheContexts() {
509 $cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
510 ->disableOriginalConstructor()
512 $cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE);
514 $container = new ContainerBuilder();
515 $container->set('cache_contexts_manager', $cache_contexts_manager);
516 \Drupal::setContainer($container);
518 // There are no cache contexts by default.
519 $this->assertEquals([], $this->entity->getCacheContexts());
521 // Add an additional cache context.
522 $this->entity->addCacheContexts(['user']);
523 $this->assertEquals(['user'], $this->entity->getCacheContexts());
527 * @covers ::getCacheMaxAge
528 * @covers ::mergeCacheMaxAge
530 public function testCacheMaxAge() {
531 // Cache max age is permanent by default.
532 $this->assertEquals(Cache::PERMANENT, $this->entity->getCacheMaxAge());
534 // Set two cache max ages, the lower value is the one that needs to be
536 $this->entity->mergeCacheMaxAge(600);
537 $this->entity->mergeCacheMaxAge(1800);
538 $this->assertEquals(600, $this->entity->getCacheMaxAge());