3

I've got a form class, and I'm injecting my custom functionality class into it via constructor. That class is added to the container via the services.yml file defined in my custom module.

I'd like to test that the form is functioning correctly, but for testing purposes I'd like to replace the class that's injected into the form with a fake one.

Both real and fake classes implement the same interface. The form is working with that interface.

My test file:

<?php
/**
 * @file
 * Contains \Drupal\my_module\Tests\FormTest.
 */

namespace Drupal\my_module\Tests;


use Drupal\my_module\Tests\Fake\FakeClass;
use Drupal\simpletest\WebTestBase;

class FormTest extends WebTestBase
{

    public static $modules = [
        'my_module',
    ];

    public function setUp()
    {
        parent::setUp();

        $fake = new FakeClass();

        $this->container->set('my_module.real_class', $fake);

        // $this->container->get('my_module.real_class') returns a FakeClass object.
    }

    public function testFormWorks()
    {
        $this->drupalGet('my-form');
        $this->drupalPostForm(NULL, [], 'Submit');
        $this->assertRaw('Form is working.');
    }

}

When I'm running the test, the real class is being used, which is not the desired test behaviour.

I can't figure out what I'm doing wrong here. Any help is appreciated.

Update: the issue is not specific to forms. Same thing happens in page controllers.

  • Interested in this as well. At the moment, I use phpunit to do something similar, but I cannot simulate a full form post because FormBuilder always resets form state. Instead I have a test for validate and submit separately and create mocks of form state. – mradcliffe Apr 5 '16 at 21:00
  • I don't believe that it has anything to do with the FormBuilder. I've reproduced the issue with a page controller. I created a test page which outputs the name of the injected class. Fake class never gets injected during the web test. A bug in WebTestBase maybe? – mkudenko Apr 5 '16 at 22:15
1

Here's the solution.

Let's say your module A defines a new service.

In order to alter that service, you need to create a separate module B and follow the steps outlined in Overriding the default service class section on https://api.drupal.org/api/drupal/core%21core.api.php/group/container/8.

Make sure to list module A as a dependency in module B info.yml file.

Add both modules to the $modules array in your test class, and everything will work. $this->container->set() is no longer needed.

That issue on drupal.org: https://www.drupal.org/node/2700739

  • 1
    Yes, that's one way to do it. Note that just defining an overriding services.yml file in another module assumes that the module comes later, which by default depends on the name. – Berdir Apr 6 '16 at 9:38
  • 1
    You can also do it without a test module like that. See github.com/md-systems/redis/blob/8.x-1.x/src/Tests/…. Basically, you write a services.yml file into the sites folder (each test has its own folder), then it is guaranteed to come later. – Berdir Apr 6 '16 at 9:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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