6

Simple as this:

class A {
  use StringTranslationTrait;
  
  public function something() {
    $b = 1+2;
    $this->t($b);
    return $b;
  }
}

I would like to unit test the something() method, but I can't since the StringTranslation trait uses a static container call:

Drupal\Core\DependencyInjection\ContainerNotInitializedException: \Drupal::$containe is not initialized yet. \Drupal::setContainer() must be called with a real container.

What do you suggest?

5 Answers 5

7

StringTranslationTrait also has a setStringTranslation() method so you can inject a string translation mock for exactly that purpose.

1
  • 1
    Could you update or link to an example, please?
    – Kevin
    Nov 17, 2016 at 16:04
10

Following on Bedir's answer, here is an example.

I have a test that is using the class DurationService. DurationService uses the StringTranslationTrait as follows:

class DurationService implements DurationServiceInterface {
  use StringTranslationTrait;
  ...

To test this, I am doing the following (note, using a class that extends UnitTestCase):

$duration_service = new DurationService();
$duration_service->setStringTranslation($this->getStringTranslationStub());

This calls StringTranslationTrait::setStringTranslation() passing it the response from UnitTestCase::getStringTranslationStub().

2
  • Thank you for the example. Good extension for Berdir's answer.
    – ssibal
    Jun 1, 2018 at 18:49
  • For reference, note that you need to use $this->t() and not just t().
    – NicklasF
    May 25, 2021 at 8:14
1

Here is how I solved it, note your test must extend Drupal\Tests\UnitTestCase:

  /**
   * {@inheritdoc}
   */
  public function setUp() {
    parent::setUp();
    $container = new ContainerBuilder();
    \Drupal::setContainer($container);
    $container->set('string_translation', self::getStringTranslationStub());
  }
2
  • I feel this an unnecessary extra step: Your class has the "setStringTranslation" method, that is just one call. Also don't forget you don't have a valid container and services in a "pure" unittest.
    – ssibal
    Oct 5, 2018 at 8:58
  • Interesting, as I got this from looking at Unit tests in core.
    – Cameron
    Oct 8, 2018 at 3:14
0

By implementing the ContainerFactoryPluginInterface you can use dependency injection to fake the string translation service.

class A implements ContainerFactoryPluginInterface { 
  use StringTranslationTrait; 

  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, TranslationInterface $string_translation);
    $this->stringTranslation =$string_translation;
  }

  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static($configuration, $plugin_id, $plugin_definition, $container->get('string_translation'));
  }

  public function something() { 
    $b = 1+2; 
    $this->t($b); 
    return $b; 
  } 
}
6
0

Just take a look at the FormValidatorTest:

For the class

/**
 * Provides validation of form submissions.
 */
class FormValidator implements FormValidatorInterface {

  use StringTranslationTrait;

  public function __construct(RequestStack $request_stack, TranslationInterface $string_translation, CsrfTokenGenerator $csrf_token, LoggerInterface $logger, FormErrorHandlerInterface $form_error_handler) {
    $this->requestStack = $request_stack;
    $this->stringTranslation = $string_translation;
    $this->csrfToken = $csrf_token;
    $this->logger = $logger;
    $this->formErrorHandler = $form_error_handler;
  }

This is the test

  /**
   * Tests the 'validation_complete' $form_state flag.
   *
   * @covers ::validateForm
   * @covers ::finalizeValidation
   */
  public function testValidationComplete() {
    $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
      ->setConstructorArgs([new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
      ->setMethods(NULL)
      ->getMock();

    $form = [];
    $form_state = new FormState();
    $this->assertFalse($form_state->isValidationComplete());
    $form_validator->validateForm('test_form_id', $form, $form_state);
    $this->assertTrue($form_state->isValidationComplete());
  }

You can use as constructor param $this->getStringTranslationStub()

2
  • This is not an answer to the original question: I don't have string translation argument in my constructor.
    – ssibal
    Jun 1, 2018 at 6:48
  • @ssibal. I see, I misread the question. Jun 1, 2018 at 11:20

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.