How to use additionProvider with repository functions? - php

I am trying to configure test classes for my Symfony 2.7 project. I am testing a controller that uses doctrine for connecting to the data base.
I finally managed to do it extending KernelTestCase in order to avoid this Fatal error: Call to a member function "X" on a non-object. But here is the problem: I was triying to order my code and simplify 5 test functions into one by using a additionProvider:
public function additionProvider()
{
$first=$this->service->getTranslation("","en");
return array
(
'original not created' =>array($first,"")
);
}
and I want to use it like:
/**
* #dataProvider additionProvider
*/
public function testGetTranslation($expected, $actual)
{
$this->assertEquals($expected, $actual);
}
here is my setUp():
public function setUp()
{
self::bootKernel();
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager()
;
$this->service = new \DictionaryBundle\Controller\UtilController($this->em);
}
I tried to add the first test like this and the error appeared again, like if it couldn't access to the repository. So, is possible to use addittionProviders with repository functions? how?
Thanks!

The dataProvider is executed before the setup method. So the service variable is not yet initialized.
So the dataprovider method can only return data, you need to move the call to the service in the tested method.
Here the paragraph of the doc:
Note All data providers are executed before both the call to the
setUpBeforeClass static method and the first call to the setUp method.
Because of that you can't access any variables you create there from
within a data provider. This is required in order for PHPUnit to be
able to compute the total number of tests.

Related

Difference between User::class and new User

I am using Laravel 8. I want to know the difference between new User() and User::class because I am having trouble while using new User().
Here is the scenario,
I have UserServices class in which I am injecting UserRepository class
class UserServices
{
private $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function findUserByEmail($email){
return $this->userRepository->findUserByEmail($email);
}
}
Now in service provider when I bind the UserServices class using following way I am getting following error
ArgumentCountError: Too few arguments to function
$this->app->bind(UserServices::class, function($app){
return new UserServices();
});
But when I use UserServices::class then it works fine
$this->app->bind(UserServices::class);
Why?
I know that UserServices class constructor is expecting parameter then why working with UserServices::class
//Working
$this->app->bind(UserServices::class);
//Not Working
$this->app->bind(UserServices::class, function($app){
return new UserServices();
});
In the first case, you're providing an explicit constructor which attempts to return an instance of the class, but does it incorrectly.
In the second case, you're leaving it up to the service container to determine dependencies and inject them automatically, which does it correctly.
If you are using an interface then you need to bind the interface with the related class in the service provider. Otherwise you don't need any binding

Laravel Own ServiceProvider Client Call Type error: Argument 1 passed to ... must be an instance of

I want to swap out my client call or better i try to make a wrapper around this package, so i dont have to write this everytime, so i made a new ServiceProvider which should call
// Create a new client,
// so i dont have to type this in every Method
$client = new ShopwareClient('url', 'user', 'api_key');
on every request i make.
// Later after the Client is called i can make a Request
return $client->getArticleQuery()->findAll();
SwapiServiceProvider
<?php
namespace Chris\Swapi;
use Illuminate\Support\ServiceProvider;
use LeadCommerce\Shopware\SDK\ShopwareClient;
class SwapiServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* #return void
*/
public function boot()
{
}
/**
* Register any package services.
*
* #return void
*/
public function register()
{
$this->app->singleton(ShopwareClient::class, function () {
return new ShopwareClient(
env('SHOPWARE_URL'),
env('SHOPWARE_USER'),
env('SHOPWARE_KEY')
);
});
}
}
My Class
...
use LeadCommerce\Shopware\SDK\ShopwareClient as Shopware;
class Swapi
{
public function fetchAllArticles(Shopware $shopware)
{
return $shopware->getArticleQuery()->findAll();
}
}
Testing
I just call it in my routes.php for testing
use Chris\Swapi\Swapi;
Route::get('swapi', function () {
// Since this is a package i also made the Facade
return Swapi::fetchAllArticles();
});
But i get everytime the error
FatalThrowableError in Swapi.php line 18: Type error: Argument 1
passed to Chris\Swapi\Swapi::fetchAllArticles() must be an instance of
LeadCommerce\Shopware\SDK\ShopwareClient, none given, called in
/Users/chris/Desktop/code/swapi/app/Http/routes.php on line 7
So i am asking why this
return new ShopwareClient(
env('SHOPWARE_URL'),
env('SHOPWARE_USER'),
env('SHOPWARE_KEY')
);
is not called everytime i call a method e.g $shopware->getArticleQuery()->findAll();
Does anyone know why?
I think there might be some confusion here about Laravel's IoC. When you use return Swapi::fetchAllArticles();, Laravel doesn't know what you are doing because you haven't used the container to build out the Swapi class (even though you have registered one with the container) nor do you have a facade built to access it in that manner. Otherwise PHP is going to complain because your function isn't static.
I just wrote this code and verified that it works as far as Laravel putting it all together.
In my service provider, my register function was this...
public function register()
{
$this->app->singleton('swapi', function($app) {
return new SwapiRepository(
new ShopwareClient(
env('SHOPWARE_URL'),
env('SHOPWARE_USER'),
env('SHOPWARE_KEY')
)
);
});
}
Keep in mind, swapi is really just a key the container will use to find the actual class. There's no need to pass in the entire qualified class name when you can keep it simple and easy.
My SwapiRepository which is really the wrapper for the Shopware SDK.
use LeadCommerce\Shopware\SDK\ShopwareClient;
class SwapiRepository
{
protected $client;
public function __construct(ShopwareClient $client)
{
$this->client = $client;
}
public function fetchAllArticles()
{
return $this->client->getArticleQuery()->findAll();
}
}
At this point, you are basically done. Just add App\Providers\SwapiServiceProvider::class, in the providers array (which you probably have done already) in app/config.php and use your wrapper like so...
$swapi = app('swapi');
$swapi->fetchAllArticles();
Or you can have Laravel inject it into other classes as long as Laravel is building said class.
If you want to build out a facade for this to save yourself a line of code each time you want to use this or for snytactical sugar...
use Illuminate\Support\Facades\Facade;
class Swapi extends Facade
{
protected static function getFacadeAccessor() { return 'swapi'; }
}
Make sure to update your aliases array in app/config.php so that it contains 'Swapi' => App\Repositories\Swapi::class,
And finally you should be able to use it like so...
Swapi::fetchAllArticles();
Please note your namespaces are different than mine so you may need to replace mine with yours. You should also now be able to easily inject Swapi into other classes and even method injected into your controllers where needed.
Just remember if you do that though, make sure you are grabbing instances of those classes from Laravel's service container using the app() function. If you try to build them out yourself using new SomeClass, then you have the responsibility of injecting any dependencies yourself.

How to access get method in a Test Class Symfony2

I have to write a test class to test my service methods. In my controllers I could access the service by doing $service = $this->get('myService'); and I could access my methods by doing $service->someMethod();.
Now I want to write a test class to test some of the service methods, I tried doing like the doc :
class ServiceTest extends \PHPUnit_Framework_TestCase {
public function testSomeMethod() {
$service = $this->get('myService');
....
}
}
When I launch the test I get something like : Fatal error : Call to undefined method XXX\XXXBundle\Tests\Services\ServicesTest::get() in ...
So my question is how can I use get method to be able to call my service methods
You don't need to use the container in order to test services and other classes.
You should create a new instance of the class and inject the dependencies using test doubles.
Test doubles
- since you don't want to write a functional testing and to use the real dependencies you should be using a test double.
for example if one of the service dependencies is the EntityManager what would happen is that a new entries would be inserted into the Database (or deleted from it) and its not the purpose of Unit testing (you do need to check this tho if you are writing a functional test).
And that's how you should approach this kind of testings:
public function testSomeServiceAdd()
{
$dependencyOne = $this->getMockBuilder('Acme\SomeBundle\DependencyOne')->
disableOriginalConstructor()->
getMock();
$dependencyTwo = $this->getMockBuilder('Acme\SomeBundle\DependencyTwo')->
disableOriginalConstructor()->
getMock();
$service = new SomeService($dependencyOne, $dependencyTwo);
$response = $service->add(1, 2);
$this->assertEquals($response, 3);
}
As you can see I am "mocking" the dependencies and injecting it into the service, after that i'm calling the method and asserting the result.
I would also like to suggest a Mocking framework instead of using the built-in PHPUnit mocking functions. it's easier to use and it has much more mocking functionality:
https://github.com/danrevah/ShortifyPunit
To test your services you should extend the KernelTestCase class.
class ServiceTest extends KernelTestCase
{
private $service;
/**
* {#inheritDoc}
*/
public function setUp()
{
self::bootKernel();
$this->service = static::$kernel->getContainer()
->get('my_service')
;
}
}

Illuminate components in packages

I have a self-made package. With the service provider I pass the $app instance to the actual class. Then I use:
$this->app['session']->get('key', 'value');
to use the Session component's functionality. However, when I run this, I get the following error:
Using $this when not in object context
So I tried just using
Session::get( ... )
but then it says it doesn't find the class.
How do I solve this?
This is my full class:
namespace AlexCogn\Messenger;
use Illuminate\Foundation\Application as Application;
class Messenger
{
protected $app;
public function __construct(Application $app)
{
$this->app = $app;
}
public static function messages()
{
$messages = $this->app['session']->get('messages', array());
$this->app['session']->forget('messages');
return $messages;
}
public function addMessage($message)
{
$messages = $this->app['session']->get('messages', array());
array_push($messages, $message);
$this->app['session']->put('messages', $messages);
}
}
EDIT:
apparently not everyone understands the question: I am calling Messenger through a Facade:
Messenger::addMessage('test');
and I thought Laravel converted this to
$app['messenger']->addMessage('test');
So shouldn't this be called via an instance?
You are trying to call $this from a static method within your class. This won't work. By PHP definition (and also because it'd be stupid otherwise), $this is only available in the following scopes:
Inside a method that has been called through an instance (i.e. $this->foo())
Inside a class constructor (inside __construct())
Inside a Callable where this has been redefined using the Runkit library
Anything else causes this to trigger the fatal error you have just received. I cannot suggest a fix, as you did not put your code up - however, if you do, I'll be more than happy to find a solution for you.

Getting a mock Doctrine\ORM\QueryBuilder

I've been trying all day to write some tests for a class that parses a custom DSL and creates DQL queries.
My class requires that I pass in a queryBuilder which it then uses to build the query and return it.
The problem I have is that
to get a queryBuilder I need an entityManager
to get an entityManager I need a connection
to get a connection I need a database
I'd rather that my unit tests didn't rely on a database so I've been trying to create a mock queryBuilder using PHPUnit but queryBuilder seems to rely on the entityManager which in turn relies on a connection.
So 2 questions:
Is there a better way to dynamically construct queries in Doctrine 2?
Is there a way to get a working queryBuilder without an entityManager (or at least without a real database)?
I'm not sure how to do it with PHPUnit, but I would recommend checking out Mockery for your PHP mocking needs. It's a really powerful library which allows you to mock the EntityManager pretty easily, and therefore anything that it will return.
For your use case, you can easily create a Mock entity manager than will return you a mock query builder:
$mockEm = \Mockery::mock('\Doctrine\ORM\EntityManager');
$mockQb = \Mockery::mock('\Doctrine\ORM\QueryBuilder');
$mockEm->shouldReceive('createQueryBuilder')->andReturn($mockQb);
Then you can use Mockery's core functionality to set the expectations you require for your unit test.
On a slight aside, you also get the option of using a 'passive' EntityManager mock, in case you want to unit test a function which contains some database logic, but you don't actually want to go to the database in your unit test.
public function getPassiveEntityManagerMock() {
return \Mockery::mock('\Doctrine\ORM\EntityManager',
array('getRepository' => new FakeRepository(),
'getClassMetadata' => (object)array('name' => 'aClass'),
'persist' => null,
'flush' => null));
}
Here's a suggested way of doing it with PHPUnit, but I haven't tried it out myself.
I ended up creating my own mock query builder; it may have been possible to get the same functionality using PHPUnits mocking framework but this is wha I ended up with:
<?php
namespace MyNameSpace\Tests\Mocks;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Common\Collections\ArrayCollection;
class MockQueryBuilder extends QueryBuilder {
protected $expr;
protected $paramReflect;
public function __construct($expr) {
$this->expr = $expr;
$this->paramReflect = new \ReflectionProperty('Doctrine\ORM\QueryBuilder', 'parameters');
$this->paramReflect->setAccessible(true);
$this->paramReflect->setValue($this, new ArrayCollection());
}
/*
* #return Query\Expr
*/
public function expr() {
return $this->expr;
}
public function getEntityManager() {
return null;
}
public function getQuery() {
return array(
'parameters' => clone $this->paramReflect->getValue($this),
'dql' => $this->getDQL(),
);
}
}

Categories