I am writing codeception unit tests for a Manager class in my Symfony2 application, and I am wondering how to mock the entity manager. For example, let's say I have the following function in my AcmeManager service class:
<?php
namespace Acme\AcmeBundle\Manager;
use Doctrine\Common\Persistence\ObjectManager;
class AcmeManager
{
private $em;
public function __construct (ObjectManager $em)
{
$this->em = $em;
}
public function findMatches($index)
{
// Find and display matches.
$matches = $this->em
->getRepository('AcmeBundle:AssignMatch')
->findBy(array('assignIndex' => $index));
return $matches;
}
}
and I wanted to write the following test function:
<?php
use Codeception\Util\Stub;
class AutoManagerTest extends \Codeception\TestCase\Test
{
/**
* #var \CodeGuy
*/
protected $codeGuy;
protected function _before()
{
}
protected function _after()
{
}
/**
* Tests findMatches($index).
*/
public function testFindMatches()
{
//... $mockedEntityManager is our mocked em
$acmeManager = $this->getModule('Symfony2')->container->get('acme_manager');
$acmeManager->findMatches(0);
// $this->assert(isCalled($mockedEntityManager));
}
}
How can I mock the entity manager such that when I call $acmeManager->findMatches(0);, I can assert that the mocked entity manager is called (even though $acmeManager uses the regular Symfony2 entity manager in its normal implementation)?
I think the easiest way would be to skip the DIC part and simply instantinate the AcmeManager with the EM passed in the constructor.
The other way would be getting it from the DIC as you currently do, and setting AcmeManager::$em with reflection. Something like this:
$acmeManager = $this->getModule('Symfony2')->container->get('acme_manager');
$class = new \ReflectionClass('\Acme\AcmeBundle\Manager\AcmeManager');
$property = $reflection->getProperty('em');
$property->setAccessible(true);
$property->setValue($acmeManager, $mockedEntityManager);
Related
I'm extracting code from Controller to a kind of ApplicationService in a Symfony 3.4 App.
I've a concrete class for scraping data and another concrete transformer to change some data.
src\App\Service
class CompanyScraping implements ScrapingInterface
{
private $crawler;
public function __construct(CrawlerInterface $crawler)
{
$this->crawler = $crawler;
}
public function extract()
{
...
}
public function transform()
{
$transformer = new concreteTransformer();
}
}
class concreteTransformer
{
private $em;
public __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
}
How could I pass the EntityManager to the concreteTransformer class if the EntityManager is not called in CompanyScraping class? I can't instantiating concreteTransformer with a new.
I'm thinking in this two options:
Pass EntityManager to CompanyScraping, but I thik that is a wrong idea because CompanyScraping doesn't need this dependency.
Extract transform method into a another class and pass the em from controller/console
$crawler = new CompanyScraping(new GoutteClient());
$rawData = $crawler->extract(...);
$data = new concreteTransformer($em, $rawData);
Any other idea?
Thanks.
The first solution I thought of is to inject (not the EM, but) the Transformer into the Scraper class, as posted in the comments.
Such solution would, however, not address the underlying problem: is the Scraper scraping, transforming or both? In the latter case, it's not adhering to the single responsibility principle, because it has the responsibility for both scraping and transforming.
A design pattern that could be pretty effective to tackle this, is the decorator pattern.
The idea in this case is to "decorate" the scraped results with a transformation.
The result would look somewhat like this:
class Transformer implements ScrapingInterface
{
private $scraper;
private $em;
public __construct(ScrapingInterface $scraper, EntityManagerInterface $em)
{
$this->scraper = $scraper;
$this->em = $em;
}
public function extract()
{
return $this->transform($this->scraper->extract());
}
private function transform() {...}
}
It can be constructed as:
$crawler = new Transformer(new CompanyScraping(new GoutteClient()), $em);
In case you have multiple transformer implementations, you can make the decorator more generic:
class TransformingScraper implements Scraper
{
private $scraper;
private $transformer;
public __construct(Scraper $scraper, Transformer $transformer)
{
$this->scraper = $scraper;
$this->transformer = $transformer;
}
public function extract()
{
return $this->transformer->transform($this->scraper->extract());
}
}
$crawler = new TransformingScraper(
new CompanyScraping(new GoutteClient()),
new ConcreteTransformer($em)
);
I'm wondering if this is the correct way to extend and use classes with Symfonies autowiring.
For example, I have a BaseClass that instantiates and auto wires the entity manager.
class BaseClass
{
protected $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
protected function someMethodIWantToUse(Entity $something)
{
// Do something there
$this->entityManager->persist($something);
$this->entityManager->flush();
}
}
Then I have a subclass that extends the BaseClass and needs access that method. So I let it autowire again and pass it to the parent constructor.
class SubClass extends BaseClass
{
private $handler;
public function __construct(EntityManagerInterface $em, SomeHandler $handler)
{
parent::__construct($em);
$this->handler = $handler;
}
public function SubClassMethod()
{
// Get some data or do something
$entity = SomeEntityIGot();
$this->someMethodIWantToUse($entity);
}
}
Now I'm wondering if this is actually the correct way to do this or there's something I'm missing and the parent class should be able to autowire the entitymanager by itself?
To summarize the comments, yes your way is correct. Depending on your use case there are alternatives.
This are the ways you can go about it:
1. Extending Class and using Constructor Injection (what you do)
class BaseClass {
protected $some;
public function __construct(SomeInterface $some)
{
$this->some = $some;
}
}
class SubClass extends BaseClass {
private $other;
public function __construct(SomeInterface $some, OtherInterface $other)
{
parent::__construct($some);
$this->other = $other;
}
}
2. Setter Injection
class BaseClass {
protected $some;
public function __construct(SomeInterface $some)
{
$this->some = $some;
}
}
class SubClass extends BaseClass {
private $other;
public function setOther(OtherInterface $other)
{
$this->other = $other;
}
}
Now setOther won't automatically be called, you have to "manually" call it by either specifying a calls property in your services.yaml file, as described here: https://symfony.com/doc/current/service_container/calls.html. This would then look something like this:
// services.yaml
App\SubClass:
calls:
- [setOther, ['#other']]
Or
// services.yaml
app.sub_class:
class: App\SubClass
calls:
- [setOther, ['#other']]
assuming, an implementation of OtherInterface is available as #other in the service container.
A more elegant solution if you're using autowiring, simply add a #required annotation to the function as described here: https://symfony.com/doc/current/service_container/autowiring.html#autowiring-calls, which would look like this:
/**
* #required
*/
public function setOther(OtherInterface $other)
{
$this->other = $other;
}
3. Property Injection
class BaseClass {
protected $some;
public function __construct(SomeInterface $some)
{
$this->some = $some;
}
}
class SubClass extends BaseClass {
public $other;
}
As with the Setter Injection, you'll need to tell Symfony to populate this property, by specifying it in your services.yaml file like this:
// services.yaml
App\SubClass:
properties:
other: '#other'
Or
// services.yaml
app.sub_class:
class: App\SubClass
properties:
other: '#other'
assuming, an implementation of OtherInterface is available as #other in the service container.
Conclusion:
Since there are different ways to solve this, it's up to you to determine the correct way for your use case. I personally go with either option 1 (Constructor Injection) or option 2 (Setter Injection) using the annotation. Both of them allow you to use typehints and thus allowing your IDE to help you write clean code.
In 90% of cases, I'd go with option 1, as then it's clear for every one reading your code, what services are available with one glance at the __constructor function.
One use case for Setter Injection would be a base class offering all the setXXX functions but then sub classes not needing all of them. You could have a constructor in each sub class, requesting the needed services and then calling the setXXX methods of the base class.
Note: this is kind of an edge case and you probably won't run into this.
You can find a list of advantages and disadvantages of each method directly in the Symfony documentation about the Service Container -> Types of Injection
This way too :
class BaseClass
{
protected Environment $twig;
#[Required]
public function setTwig(Environment $twig): void
{ $this->twig = $twig; }
}
class ChildClass extends BaseClass
{
public function __construct(
private EntityManagerInterface $entityManager
) { }
public function test()
{
$this->twig->render(......);
}
}
I am trying to inject one service into another to call function from it but it throws an error:
Type error: Argument 1 passed to App\Base\Service\Shop\ShopService::__construct() must be an instance of App\Base\Service\AccountService, instance of ContainerSgqv3vy\appDevDebugProjectContainer given
And I think I implement everything correctly:
use App\Base\Service\AccountService;
use App\Base\Service\BaseService;
class ShopService extends BaseService
{
/**
* #param AccountService $accountService
*/
public function __construct(AccountService $accountService)
{
parent::__construct();
$this->accountService = $accountService;
}
And calling in my function from it:
this->accountService->getMyFunction();
And my instantiated class :
class BaseService
{
/** #var ContainerInterface */
var $container;
var $em;
public function __construct(ContainerInterface $container, EntityManagerInterface $em)
{
$this->container = $container;
$this->em = $em;
}
service.yml
app.shop:
class: App\Base\Service\ShopService
arguments: ["#service_container", "#doctrine.orm.default_entity_manager"]
public: true
When you extend a class that has constructor arguments, you should keep those arguments and add any extra argument after.
Example:
class BaseClass
{
public function __construct(Foo $foo, Bar $bar) {
// ....
}
}
To instantiate this class, you would need to pass the container and the entity managaer:
$base = new BaseClass($fooInstance, $barInstance);
If you want to extend this class, it will most likely still need those dependencies + our new dependency:
class ChildClass extends BaseClass
{
public function __construct(Foo $foo, Bar $bar, Duck $duck)
{
// The parent (BaseClass) requires Foo and Bar,
// or it will throw an error
parent::__construct($foo, $bar);
$this->duck = $duck;
}
}
To instantiate our new ChildClass, we would need to pass three arguments:
$child = new ChildClass($fooInstance, $barInstance, $duckInstance);
Then, when you define our ChildClass in the service.yml, you need to have define all three dependencies:
app.childclass:
class: ChildClass
arguments: ["Foo", "Bar", "Duck"]
...
Since I'm not using Symfony myself, you must excuse for not knowing the exact syntax of service.yml, but the concept is the same.
I want to mock a method foo in a class but leave method bar as it is:
<?php
class MyClass
{
protected $dep1;
protected $dep2;
protected $dep3;
protected $dep4;
/**
* Test constructor.
* #param $dep1
* #param $dep2
* #param $dep3
* #param $dep4
*/
public function __construct($dep1, $dep2, $dep3, $dep4)
{
$this->dep1 = $dep1;
$this->dep2 = $dep2;
$this->dep3 = $dep3;
$this->dep4 = $dep4;
parent::__construct();
}
public function foo()
{
return "foo";
}
public function bar()
{
return "bar";
}
}
However, MyClass is instantiated via a factory which retrieves the dependencies ($dep1, $dep2, ...) and inject them directly in to the constructor of MyClass.
So I want to use that Factory and instantiate a MyClass-object (otherwise the complex instantiation has to be coded in the test case also).
In short I want to know if there is another solution than:
class TestMyClass extends \PHPUnit\Framework\TestCase {
public function setUp()
{
// complicated way to retrieve $dep1 to $dep4
$mock = $this->getMockBuilder(MyClass::class)->setMethods(['foo'])->setConstructorArgs([$dep1, $dep2, $dep3, $dep4])->getMock();
$mock->expects($this->any())->method('foo')->willReturn(false);
}
}
Is it possible to somehow create proxy to an already created instance of MyClass, which just overrides the foo method? Is there another way (without runkit pecl extension) to mock an method of an existing/instantiated object?
I know PHPUnit has some Proxy-related methods, but I couldn't find any documentation / example of usage of them, so I am not sure if they even could be used to solve my problem.
I have 2 tables in DB (question and answer). One question has many answers.
I get some Answers and depends on question.type I prepare results array.
In app without any framework I have Factory class which return specific object (SingleChoiceQuestion, OpenQuestion, MultipleChoiceQuestion) depends question.type from DB. All Questions extends abstract class Question which has declared abstract method getResults. All types have their own business logic to prepare results.
So in this situation when I have created object by factory I'm using method getResults and everything works well.
I would like to create it in Symfony and I read documentation. In my opinion I should create services for all my Question types.
I have created AggregatedResultsManager with method generate which returns results array. Depends on question.type it calls getResults method from specific service.
I would like to add, that I can't change DB structure.
My questions:
Am I creating and using services right? If I do it wrong, please help me understanding it and show me the right way.
I will have several services like AggregatedResultsManager and about 18 Question types.
In each service I will need to create switch with 18 choices, how to prevent that?
switch ($this->question->getType()) {
case Question::SINGLE:
$results = $this->container->get('app.single_choice_question')->getResults($answers);
break;
// other types
}
I have some idea to create array with types and service names:
$services = [
Question::SINGLE => 'app.single_choice_question',
Question::MULTIPLE => 'app.multiple_choice_question',
Question::OPEN => 'app.open_question',
];
and then use it in each service like that:
$results = $this->container->get($services[$this->question->getType()])->getResults($answers);
I think it's the best way to not use switch with 18 choices. But I will need to hardcode service names in array.
My code:
services.yml
app.question:
class: AppBundle\Questions\Question
abstract: true
arguments: ['#doctrine.orm.entity_manager']
app.single_choice_question:
class: AppBundle\Questions\SingleChoice
parent: app.question
app.agreggated_results_manager:
class: AppBundle\Results\AggregatedResultsManager
arguments: ['#doctrine.orm.entity_manager', '#service_container']
abstract Question
abstract class Question
{
/**
* #var EntityManager
*/
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
abstract public function getResults($answers);
}
SingleChoice
class SingleChoice extends Question
{
public function getResults($answers)
{
$results = [];
// business logic
return $results;
}
}
Results
class AggregatedResultsManager
{
/**
* #var EntityManager
*/
private $em;
/**
* #var Question
*/
private $question;
/**
* #var ContainerInterface
*/
private $container;
public function __construct(EntityManager $em, ContainerInterface $container)
{
$this->em = $em;
$this->container = $container;
}
public function generate()
{
if (!$this->question) {
throw new \LogicException('Question is not set');
}
$answers = $this->em
->getRepository('AppBundle:Answer')
->findBy(['question' => $this->question]);
$results = [];
if (empty($answers)) {
return $results;
}
switch ($this->question->getType()) {
case Question::SINGLE:
$results = $this->container->get('app.single_choice_question')->getResults($answers);
break;
// other types
}
return $results;
}
public function setQuestion(Question $question)
{
$this->question = $question;
}
}
Controller
public function questionIdsAction(Question $question)
{
$resultsManager = $this->get('app.agreggated_results_manager');
$resultsManager->setQuestion($question);
$results = $resultsManager->generate();
return new JsonResponse($results);
}
I think you are saying that you have 18 QuestionTypes all extending an AbstractQuestion which needs the entity manager to do it's work? Instead of making 18 services and then using the container I would suggest making a question factory:
class QuestionFactory
public function __construct($entityManager)
$this->entityManager = $entityManager;
public function create($questionType)
switch($questionType) {
case Question::SINGLE: return new SingleQuestion($this->entityManager);
You would then inject the factory into the results manager.
This approach avoids the need to create a bunch of services and of needing to pass around the container. You still have a switch statement but that is okay.
The only problems that might arise is if some QuestionTypes need additional dependencies. In which case you might be back to using services.