Efkiss
Efkiss

Reputation: 115

How to instantiate autowired services in Symfony?

I want to instantiate OrderAbstraction service which requires entity manager in constructor in a test case.

class OrderAbstraction
{
    ...

    /**
     * @var EntityManager
     */
    private $em;

public function __construct(EntityManager $em)
    {
        $this->em = $em;
        ...
    }
}

I've tried to autowire OrderAbstractionTest and give a OrderAbstraction and entity manager as parameter.

class OrderAbstractionTest extends TestCase
{
// tried with constructor and without it
    public function testDays(){
        $order = new OrderAbstraction();
        $order->dateFrom(new \DateTime('2019-08-20 14:00'));
        $order->dateTo(new \DateTime('2019-08-28 14:00'));

        $days = $order->days();
        $this->assertEquals(8, $days);
    }
}

my service.yaml, autowire set to true

    App\Service\OrderAbstraction\:
        resource: '../src/Service/OrderAbstraction.php'
        arguments: 
            $em: '@Doctrine\ORM\EntityManager'

    App\Test\OrderAbstraction\:
        resource: '../tests/OrderAbstractionTest.php'
        arguments: 
            $em : '@Doctrine\ORM\EntityManager'

I keep getting something like this:

PHP Fatal error: Uncaught ArgumentCountError: Too few arguments to function App\Test\OrderAbstractionTest::__construct(), 0 passed in /var/www/html/autocom/bin/.phpunit/phpunit-6.5/src/Framework/TestSuite.php on line 476 and exactly 1 expected in /var/www/html/autocom/tests/OrderAbstractionTest.php:13

Would be great to know how to instantiate services like OrderAbstraction to other services, tests. Because most of the I do something like this in controllers:

$order = new OrderAbstraction($this->em);

Upvotes: 0

Views: 2879

Answers (2)

Reqven
Reqven

Reputation: 1778

You don't need anything in the services.yml if autowire is set to true. Replace EntityManager by EntityManagerInterface in your OrderAbstraction.php to get an instance of EntityManager being automatically injected when your service is autowired.

OrderAbstraction.php

use Doctrine\ORM\EntityManagerInterface;

class OrderAbstraction {

  /**
   * @var EntityManager
   */
  private $em;

  public function __construct(EntityManagerInterface $em)
  {
    $this->em = $em;
    ...
  }
}

EDIT

As this would work as expected in a basic controller, you can't use the constructor of your unit test class to inject your service. According to Symfony's blog :

In Symfony 4.1, tests allow fetching private services by default. In practice, tests based on WebTestCase and KernelTestCase now access to a special container via the static::$container property that allows fetching non-removed private services:

OrderAbstractionTest.php

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use App\Service\OrderAbstraction;

class OrderAbstractionTest extends WebTestCase {

  /**
   * @var OrderAbstraction
   */
  private $order;

  public function testDays() {
    self::bootKernel();
    $this->order = static::$container->get(OrderAbstraction::class);

    $this->order->dateFrom(new \DateTime('2019-08-20 14:00'));
    $this->order->dateTo(new \DateTime('2019-08-28 14:00'));

    $days = $this->order->days();
  }
}

Tested and working fine on Symfony 4.3
See this anwser for more info about Symfony 3.4 and 4.0

Upvotes: 3

Efkiss
Efkiss

Reputation: 115

What end up working for me is php unit mock, my code now looks like this:

    public function testDays(){
        $em = $this->getMockBuilder(EntityManager::class)
        ->disableOriginalConstructor()
        ->getMock();

        $order = new OrderAbstraction($em);
        $order->dateFrom(new \DateTime('2019-08-20 14:00'));
        $order->dateTo(new \DateTime('2019-08-28 14:00'));

        $days = $order->days();
        $this->assertEquals(8, $days);
    }

Upvotes: 0

Related Questions