8

I'm working on a project where we need to have code coverage using phpunit.

I have a method that includes this code:

  $node = \Drupal::entityTypeManager()->getStorage('node')->load($node_id);

  $form['data-title']['#value'] = $node->label();

  $form['data-description']['#value'] = $node->field_description->value;

So I need to mock out the node object.

I can get the node label to work, but not the field_description->value

Here's my set up function in my test class:

/**
   * {@inheritdoc}
   */
  protected function setUp() {
    $container = new Container();
    $container->set('string_translation', $this->getStringTranslationStub());

    $renderer = $this->getMockBuilder(Renderer::class)
      ->disableOriginalConstructor()
      ->getMock();
    $renderer
      ->method('render')
      ->willReturn('Hello world');

    $container->set('renderer', $renderer);

    // Mock entity type manager.
    $this->entityTypeManager = $this->getMockBuilder(EntityTypeManagerInterface::class)
      ->disableOriginalConstructor()
      ->getMock();

    // Mock a node and add the label to it.
    $node = $this->getMockBuilder(Node::class)
      ->disableOriginalConstructor()
      ->getMock();
    $node->expects($this->any())
      ->method('label')
      ->willReturn('shaken not stirred');

    $node->expects($this->any())
      ->method('access')
      ->willReturn(TRUE);

    $node->expects($this->any())
      ->method('id')
      ->willReturn(1);


    // @todo figure out how to add a field to a mock node.
    /*$field_description = new \stdClass();

    $field_description->value = 'This is a description';

    $node->set('field_description', $field_description);*/


    $node_storage = $this->getMockBuilder(EntityStorageInterface::class)
      ->disableOriginalConstructor()
      ->getMock();
    $node_storage->expects($this->any())
      ->method('load')
      ->willReturn($node);
    $this->entityTypeManager->expects($this->any())
      ->method('getStorage')
      ->willReturn($node_storage);

    $this->uuid = $this->getMockBuilder(UuidInterface::class)
      ->disableOriginalConstructor()
      ->getMock();

    $container->set('entity_type.manager', $this->entityTypeManager);

    $entity_repository = $this->getMockBuilder(EntityRepository::class)
      ->disableOriginalConstructor()
      ->getMock();
    $entity_repository->expects($this->any())
      ->method('getTranslationFromContext')
      ->willReturn($node);

    $container->set('entity.repository', $entity_repository);

    \Drupal::setContainer($container);
    $this->form = MyForm::create($container);

  }
3
  • 1
    If you are doing a UnitTestCase and find yourself doing too many crazy mocks, you probably need to think about doing a KernelTestBase test.
    – mpdonadio
    Commented Aug 22, 2018 at 0:06
  • Also, using get() instead of relying on the magic __get() should make mocking a bit easier, but mocking content entities is hard as they are very complex structures. Agree that it might be better to write a kernel test
    – Berdir
    Commented Aug 22, 2018 at 5:51
  • For this project we're required to use unit testing. I'm not sure KernelTestBase would count.
    – oknate
    Commented Aug 22, 2018 at 11:58

2 Answers 2

10

The following should work using PHPUnit Mock Objects as in your example.

First mock FieldItemListInterface __get magic method.

$fieldDescMock = $this->getMockBuilder('\Drupal\Core\Field\FieldItemListInterface')
  ->disableOriginalConstructor()
  ->getMock();
$fieldDescMock->expects($this->any())
  ->method('__get')
  ->with('value')
  ->willReturn('blah');

Then mock the Node/ContentEntityBase __get magic method.

$node->expects($this->any())
  ->method('__get')
  ->with('field_description')
  ->willReturn($fieldDescMock);
3
  • Awesome, this was very helpful! It worked exactly as you wrote it.
    – oknate
    Commented Aug 22, 2018 at 11:48
  • 3
    You might look into prophecy, which might work with magic methods, but not static methods. It would help reduce the verbosity of the unit tests. It's a a bit of out scope of this question/answer and "opinionated" so I didn't want to mention it directly.
    – mradcliffe
    Commented Aug 22, 2018 at 12:20
  • @mradcliffe, would you be able to provide an example using prophecy ?
    – pbonnefoi
    Commented Jul 2, 2020 at 13:24
4

Mradcliffe's answer worked for me, here's my example code:

Here's what ended up working for me:

$this->node = $this->getMockBuilder(Node::class)
  ->disableOriginalConstructor()
  ->getMock();

$fieldAdsEnabled = $this->getMockBuilder(FieldItemListInterface::class)
  ->disableOriginalConstructor()
  ->getMock();
$fieldAdsEnabled->expects($this->any())
  ->method('__get')
  ->with('value')
  ->willReturn(1);
$this->node->expects($this->any())
  ->method('__get')
  ->with('field_widgets_enabed')
  ->willReturn($fieldAdsEnabled);

Note: for me using Node object and not NodeInterface helped. I suspect because otherwise __get isn't available.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.