0

I have a class ApplicationForm that extends ContentEntity. In my other class ApplicationFormForm (Poorly named, I know) I extend ContentEntityForm which in turn inherits from EntityForm. In one of those parent classes, a property called $entity is created, which is available in my class ApplicationFormForm.

The $entity property has all the methods I created on it and I use it in my submitForm method:

public function submitForm($form, FormStateInterface $form_state) {
 // Code omitted for brevity
 $stepsCompleted = $this->entity->getStepsCompleted();
}

Now, the problem arises when I try and unit test my submitForm method, because entity is null and therefore getStepsCompleted does not exist.

To get around this, I created an interface of my ApplicationForm class and turned ApplicationForm into a service, and injected it into my ApplicationFormForm class (type hinting the parameter as the newly created interface so I can mock it later).

The problem is, as mentioned previously, ApplicationForm inherits from other classes, and these classes have other parameters in their constructors which are not services. Specifically:

public function __construct(array $values, $entity_type) {

}

So when I try and inject my new service, it complains that I am missing argument 1 and 2 of the constructor. So... I'm pretty stuck. Does anyone have a solution so I can unit test my method?

Update - Here is my unit test. It's extending FormTestBase.

/**
   * Set up method which is called before any tests are
   * run. Mocks of the services that the class being tested rely on are
   * created. These mocks are then passed to the container, so they are
   * available to the methods under test.
   *
   * @return void
   */
  public function setUp() {

    parent::setUp();
    $container = new ContainerBuilder();

    $mockEntityTypeManager = $this
      ->getMockBuilder('\Drupal\Core\Entity\EntityTypeManager')
      ->disableOriginalConstructor()
      ->getMock();
    $container->set('entity_type.manager', $mockEntityTypeManager);

    $applicationForm = new ApplicationFormForm(
      $mockEntityTypeManager,
    );

    $this->applicationForm = $applicationForm;

    \Drupal::setContainer($container);

  }

  /**
   * @dataProvider submitFormDataProvider
   * @param $form array
   * @covers ::submitForm
   */
  public function testSubmitForm($form) {

    $form_state = new FormState();
    $form_state = $this->applicationForm->submitForm($form, $form_state);
   // Assertion will go here.

  }

  /**
   * @return array
   */
  public function submitFormDataProvider() {

    $form['base'] = array(
      '#type' => 'fieldset',
      '#tree' => true,
    );
    $form['base']['value'] = array(
      '#type' => 'textfield',
      '#title' => '<span class="form-required">Foo</span>',
      '#value' => '',
      '#attributes' => array(
        'data-required' => 'required'
      ),
      '#parents' => array(
        'base'
      )
    );

    return array(
      array($form)
    );

  }

As mentioned before, I can't see a way to mock the entity property like I have with EntityTypeManager as $entity is inherited from the parent class and has dependencies in the constructor.

4
  • Why are you "getting around" entity being null in your unit test? This is the wrong approach. You should mock out the dependencies for your ApplicationFormForm class. You shouldn't need to fundamentally change the way that a Content Entity works to unit test a form class. If you're having trouble with your unit test then you should post your unit test and how you are testing.
    – mradcliffe
    Commented Oct 23, 2016 at 16:21
  • 1
    @mradcliffe I've updated the answer with my unit tests. As you can see I have already mocked out EntityTypeManager, which wasn't hard because it is passed as an interface into the constructor of my ApplicationFormForm class. However, $entity is an inherited property from EntityForm and I can't figure out how to mock it.
    – chap
    Commented Oct 23, 2016 at 23:55
  • 1
    Awesome. Thank you for posting the unit test. There is a method ::setEntity, which you could use to set that property. In the normal form flow, EntityFormBuilder calls that method so that it can use FormBuilder to build the form array via ::buildForm. I think that you might run into issues with submitForm because its parent will call $this->entity->save() at some point as well so I don't want to just add use setEntity as an answer.
    – mradcliffe
    Commented Oct 24, 2016 at 13:34
  • Thanks @mradcliffe tbh, I got over having to do so much set up for this method. I'm only just starting to learn to unit testing for a couple of months now and sometimes it is just not worth the effort. If my code depends on an entity and other external things it will probably just be easier to do an integration test and then I will save unit testing for methods that don't have so many dependencies/ are easier to mock.
    – chap
    Commented Oct 25, 2016 at 1:01

0

Your Answer

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

Browse other questions tagged or ask your own question.