简体   繁体   中英

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);

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

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);
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM