I've this situation
public class MyTest extends .... {
function providerA(){
return array(array("a"));
}
function providerB(){
return array(array("b"));
}
/**
* #dataProvider providerA
*/
function testOne($a){
$c = "something";
return $c;
}
/**
* #depends testOne
* #dataProvider providerB
*/
function testTwo($b,$c){
var_dump($b);
var_dump($c);
}
}
var_dump($c) -> is always null, why?
I cannot figure out what is happened. On the pdf of phpunit I found this sentence:
"When a test depends on a test that uses data providers, the depending test will be executed
when the test it depends upon is successful for at least one data set. The result of a test that
uses data providers cannot be injected into a depending test."
How can achieve my goal or a result that work in the same way?
I'd suggest to make a static property, which will be filled with data from the testOne. Because of the #depends annotation, testTwo will not run if testOne fails. testOne will add value to the static property c which will be used in the testTwo test.
However, I think better practice is to separate both tests. Thus, the data needed for testTwo will all provided by providerB (not depended on other test).
private static $c = array(array("c"));
function providerA() {
return array(array("a"));
}
/**
* #dataProvider providerA
*/
function testOne($a) {
$c = "something";
self::$c[0][] = $c;
$this->assertTrue(true);
}
/**
* #depends testOne
*/
function testTwo() {
var_dump(self::$c);
}
Related
Let's assume that I have three entites: UserEntity, ZooEntity and AnimalEntity. In order to create zoo I have to create user, because zoo has to have owner. In order to create animal I have to create zoo.
Going further I have 3 controllers for each entity. I also have 3 test classes for controller testing.
UserControllerTest
ZooControllerTest
AnimalControllerTest
In animal test, every time, in each test (to make every test independent) I have to create user and then zoo. Therefore I created traits eg: UserTestTrait and ZooTestTrait which have createUser and createZoo(user) methods.
I was wondering about chaning those traits into services. But then where I should keep them?
Would be tests/services/ZooService a good place?
For now I such structure:
tests/Controller/Rest/ZooControllerTest
tests/Controller/Rest/traits/ZooTestTrait
Assuming that I have those services and every service should have access to eg. entity manager, how can I access that in service? eg. ZooService located in tests/services/ZooService
How can I use that service in controller? Lets assume that I would like to have it in setUp method:
protected function setUp(): void {
$kernel = self::bootKernel();
// access the zoo service, that has access to the entity manager
}
I found myself a better approach. Using DoctrineFixturesBundle is a very satisfying way of providing test data. It's even better, when you integrate the Fixtures into your Tests - which might slow them down, but the quality gain is super convenient.
see this tutorial
My AbstractControllerTest Class looks similar to this:
<?php
namespace App\Tests\Functional\Controller;
use App\DataFixtures\AbstractAppFixtures;
use App\DataFixtures\UserFixtures;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
abstract class AbstractControllerTest extends WebTestCase
{
/**
* #var ORMExecutor
*/
private $fixtureExecutor;
/**
* #var ContainerAwareLoader
*/
private $fixtureLoader;
/**
* set up before test
*/
public function setUp(): void {
$kernel = static::getKernelClass();
static::$kernel = new $kernel('dev', true);
static::$kernel->boot();
static::$container = static::$kernel->getContainer();
$this->addFixture(new UserFixtures());
}
/**
* #param AbstractAppFixtures $fixture
*/
protected function addFixture(AbstractAppFixtures $fixture) {
$add = true;
$activeFixtures = $this->getFixtureLoader()->getFixtures();
foreach ($activeFixtures as $activeFixture) {
if (get_class($activeFixture) === get_class($fixture)) {
$add = false;
}
}
if ($add) {
$this->getFixtureLoader()->addFixture($fixture);
if ($fixture instanceof DependentFixtureInterface) {
/** #var AbstractAppFixtures $parentFixture */
foreach ($fixture->getDependencies() as $parentFixture) {
if (class_exists($parentFixture)) {
$this->addFixture(new $parentFixture());
}
}
}
}
}
/**
*
*/
protected function executeFixtures() {
$this->getFixtureExecutor()->execute($this->getFixtureLoader()->getFixtures());
}
protected function getContainer() {
if (static::$container) {
return static::$container;
}
return static::$kernel->getContainer();
}
/**
* #return ORMExecutor
*/
private function getFixtureExecutor() {
if (!$this->fixtureExecutor) {
$em = $this->getEm();
$this->fixtureExecutor = new ORMExecutor($em, new ORMPurger($em));
}
return $this->fixtureExecutor;
}
/**
* #return ContainerAwareLoader
*/
private function getFixtureLoader() {
if (!$this->fixtureLoader) {
$this->fixtureLoader = new ContainerAwareLoader($this->getContainer());
}
return $this->fixtureLoader;
}
}
One thing you might want to consider is to introduce test data builder. Just because you have that dependency of Animal -> Zoo -> Owner doesn't mean that you have to deal with it in your tests. Of course, without having seen your tests, it's hard to tell. But I assume that, when testing the AnimalController, it is important that a valid Animal exists, not so much in which exact zoo with which exact owner. But even if that's the case, you could make your test data builders do the work for you.
The data builders don't deal with persistence, though. But this is a good thing, since you could use them for both, unit and functional tests.
I usually split up my test folder usually into Unit, Integration, Functional and Support. Support being support code for the tests. Regarding your question where to put the service, this would also be in Support. That being said, you probably won't even need the service you have thought of. The data builder create the entities you need in your tests, so you just have to persist it.
I have 2 services, BlueWorkerService and YellowWorkerService, both implementing the same interface, WorkerServiceInterface. Each of these services use the same entities but with different required logic.
I need to inject one of, but not both, of these classes and use them in ProcessorService so that the interface methods are called using on correct Worker. Which worker service to use is dependent on which Worker is currently being processed. I'll break it down:
Class WorkerProcessor {
private $workerService;
public function __construct(WorkerServiceInterface $workerServiceInterface)
{
$this->workerService = $workerServiceInterface;
}
public function getMixedColourWithRed() {
return $this->workerService->mixWithRed();
}
}
The worker service that is being used would be based on whether the worker being processed has the colour property of Blue or Yellow.
I know I can probably use a Factory to achieve this as described here but my problem is how to tell the factory which Worker colour I am processing?
Running on Symfony 3.4
If you need more info, just ask and I will update the question.
NOTE: I'm using Symfony 4.3.1. I'll post it like that, then I'll help you to move all code from this architecture to Symfony 3.4.
I'm using a similar concept to load different classes in my project. Let me explain first, then I'll add code under this text.
Firstly, I'm loading a custom compiler pass under src/Kernel.php (your file is app/AppKernel.php):
/**
* {#inheritDoc}
*/
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new BannerManagerPass());
}
BannerManagerPass its created under src/DependencyInjection/Compiler (in your case should be src/BUNDLE/DependencyInjection/Compiler`).
class BannerManagerPass implements CompilerPassInterface
{
/**
* {#inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->has(BannerManager::class)) {
return;
}
$definition = $container->findDefinition(BannerManager::class);
$taggedServices = $container->findTaggedServiceIds('banner.process_banners');
foreach (array_keys($taggedServices) as $id) {
$definition->addMethodCall('addBannerType', [new Reference($id)]);
}
}
}
As you see, this class should implement CompilerPassInterface. You can observe that I'm looking for specific services tagged as banner.process_banners. I'll show how I tagged services a little bit later. Then, I'm calling addBannerType method from BannerManager.
App\Service\BannerManager.php: (in your case src/BUNDLE/Service/BannerManager.php)
class BannerManager
{
/**
* #var array
*/
private $bannerTypes = [];
/**
* #param BannerInterface $banner
*/
public function addBannerType(BannerInterface $banner)
{
$this->bannerTypes[$banner->getType()] = $banner;
}
/**
* #param string $type
*
* #return BannerInterface|null
*/
public function getBannerType(string $type)
{
if (!array_key_exists($type, $this->bannerTypes)) {
return null;
}
return $this->bannerTypes[$type];
}
/**
* Process request and return banner.
*
* #param string $type
* #param Server $server
* #param Request $request
*
* #return Response
*/
public function process(string $type, Server $server, Request $request)
{
return $this->getBannerType($type)->process($request, $server);
}
}
This class has a custom method (created by me) called process(). You can name it whatever you want it, but I think that's pretty verbose. All parameters are sent by me, so don't mind. You can send whatever you want.
Now we have our Manager and compiler pass is set. It's time to set our banner types (based on my example) and tag them!
My banner types are under src/Service/Banner/Types (in your case should be src/BUNDLE/Service/WhateverYouWant/Type. This does not matter! You can change it later from services.yaml).
These types are implementing my BannerInterface. It does not matter the code under the class in this instance. One more thing that I should warn you! You should see that under BannerManager, inside the addBannerType() I'm calling $banner->getType(). This is one method inherited from BannerInterface in my case and it has a unique string (in my example I have three banner types: small, normal, large). This method can have any name, but don't forget to update it as well in your manager.
We are almost ready! We should tag them, then we are ready to try them!
Go to your services.yaml and add these lines:
App\Service\Banner\Types\:
resource: '../src/Service/Banner/Types/'
tags: [banner.process_banners]
Please see the tag!
Whatever I want to show a custom banner, I'm using a simple URL with $_GET where I keep my banner type, then I load it like this:
public function view(?Server $server, Request $request, BannerManager $bannerManager)
{
...
return $bannerManager->getBannerType($request->query->get('slug'))->process($request, $server);
}
I have a symfony entity that has a not mapped calculated field
namespace AppBundle\Entity;
class Page
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* Page count. Non-mapped
*
* #var integer
*/
protected $pageCount;
}
The $pageCount value is obtainable by consuming a remote service that will provide the value for use in the application.
I figured the best way is to use the postLoad event to handle this.
class PageListener
{
/**
* #ORM\PostLoad
*/
public function postLoad(LifecycleEventArgs $eventArgs)
{
// ...
}
}
I need to retrieve this value when loading values.
public function indexAction()
{
// I want to fetch the pageHits here
$pagesListing = $this->getDoctrine()
->getRepository('AppBundle:Pages')
->findAll();
// I don't want to fetch the pageHits here
$pagesListing2 = $this->getDoctrine()
->getRepository('AppBundle:Pages')
->findAll();
}
However, this will ALWAYS result in a call to a remote service.
There may be cases where I do not want the service to be invoked, so that it reduced a performance load on the application.
How can I fetch the remote values automatically, but only when I want to.
Your "problem" is pretty common and one of the reasons I never use Doctrine repositories directly.
Solution I would recommend
Always make custom repository services and inject Doctrine into them.
That way, if you want to merge some data from some other data source (eg. Redis, filesystem, some remote API), you have complete control over it and process is encapsulated.
Example:
class PageRepository
{
private $em;
private $api;
public function __construct(EntityManagerInterface $em, MyAwesomeApi $api)
{
$this->em = $em;
$this->api = $api;
}
public function find($id)
{
return $em->getRepository(Page::class)->find($id);
}
public function findAll()
{
return $em->getRepository(Page::class)->findAll();
}
public function findWithCount($id)
{
$page = $this->find($id);
$count = $this->myAwesomeApi->getPageCount($id);
return new PageWithCount($page, $count);
}
}
Solution I wouldn't recommend, but works :)
If you don't want to change your code structure and want to keep it as it is, you could make a really simple change that will make your pageCount be loaded only when it is necessary:
Move code from Page::postLoad method into Page::getPageCount()
Example:
public function getPageCount()
{
if (null === $this->pageCount) {
$this->pageCount = MyAwesomeApi::getPageCount($this->id);
}
return $this->pageCount;
}
This way, pageCount will only be loaded if something tries to access it.
My Budget entity has some methods to be executed on PrePersist and PreUpdate. The methods are:
/**
* #return \DateTime
*/
public function generateNextPaymentDate()
{
if ($this->getStartsAt() !== null) {
$date = new \DateTime($this->getStartsAt()->format('Y-m-d'));
return $date->add(new \DateInterval('P' . $this->getCheckFor() . 'D'));
}
}
/**
* #return decimal
*/
public function calculateTotalBudgetPrice()
{
$totalBudgetPrice = 0;
foreach ($this->getItems() as $item) {
$totalBudgetPrice += $item->getPrice();
}
return $totalBudgetPrice;
}
/**
* #return decimal
*/
public function calculateInstallmentRatePrice()
{
return $this->calculateTotalBudgetPrice() / $this->getInstallmentRate();
}
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function onPreEvents()
{
$this->setNextPaymentDate($this->generateNextPaymentDate());
$this->setInstallmentRatePrice($this->calculateInstallmentRatePrice());
$this->setTotalBudgetPrice($this->calculateTotalBudgetPrice());
}
The methods calculateInstallmentRatePrice() and calculateTotalBudgetPrice() uses the attributes of the Product entity, which is a collection form inside of Budget.
The issue I've noticed is that these methods only have their returned value persisted into the database if I modify one or more field of the Budget form. If I do not, the values from these two methods are still correct but simply not changed in the base.
I do not understand why it happens. Have I missed some logic?
If you look at the documentation for the preUpdate event you will see this info:
Changes to fields of the passed entities are not recognized by the
flush operation anymore, use the computed change-set passed to the
event to modify primitive field values
So, you would need to use the setNewValue() function to modify your entity, doing something like:
$eventArgs->setNewValue('nextPaymentDate', $this->generateNextPaymentDate());
The #depends annotation allows to express dependencies between tests:
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testOne()
{
// ...
return $client;
}
/**
* #depends testOne
*/
public function testTwo(Client $client)
{
// ...
}
}
If I want to return several values, I can return an array of values, such as:
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testOne()
{
// ...
return array($client, $node);
}
/**
* #depends testOne
*/
public function testTwo(array $items)
{
list ($client, $node) = $items;
// ...
}
}
While it works fine, the problem with this approach is that I lose the type hinting of my IDE, and would have to manually annotate the $client and $node variables so that it understands them properly.
What I'd like to do instead, is to explicitly use the return values as separate parameters in the second test:
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testOne()
{
// ...
return array($client, $node);
}
/**
* #depends testOne
*/
public function testTwo(Client $client, Node $node)
{
// ...
}
}
Is that possible?
I looked a while back and I think the answer is no. This is more of a clarity problem than anything. Because PHP only allows you to return one variable, you would need to introduce extra annotations logic at the PHPUnit level to tell it that what you are returning is not one value but an array of values.
On a side note I have found that returning objects with #depends can get tricky. If for example you had two tests that both depends on sets say #depends createUser. The result is a reference. Then testOne changes the user object it is given because it's a reference testTwo will now get a user object that has been changed.
I originally thought the idea of #depends returning values was awesome, but in practice I've stopped using it.