How to swap Laravel implementation with Behat and Mockery - laravel

I have a Behat FeatureContext for which I want to swap a Laravel implementation of a given class with a mocked one.
so I have this method, with a #beforeSuite annotation
/**
* #static
* #beforeSuite
*/
public static function mockData()
{
$unitTesting = true;
$testEnvironment = 'acceptance';
$app = require_once __DIR__.'/../../../bootstrap/start.php';
$app->boot();
$fakeDataRetriever = m::mock('My\Data\Api\Retriever');
$fakeData = [
'fake_name' => 'fake_value'
];
$fakeDataRetriever->shouldReceive('getData')->andReturn($fakeData);
$app->instance('My\Data\Api\Retriever', $fakeDataRetriever);
}
So I see the Laravel app and the fake data being swapped, but when I run Behat, it is being ignored, meaning Laravel is using the actual implementation instead of the fake one.
I'm using Laravel 4.2
Does someone know a way to swap Laravel implementations when running Behat?
The reason I need this is because the data is coming from remote API and I want the test to run without hitting the API.

I'm not too familiar with Behat besides what I just read in a quick tutorial to see if I can help found here... http://code.tutsplus.com/tutorials/laravel-bdd-and-you-lets-get-started--cms-22155
It looks like you are creating a new instance of Laravel, setting an instance implementation inside of it, then you are not doing anything with the Laravel instance. What's likely happening next is the testing environment is then going ahead and using its own instance of Laravel to run the tests on.
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use PHPUnit_Framework_Assert as PHPUnit;
use Symfony\Component\DomCrawler\Crawler;
use Illuminate\Foundation\Testing\ApplicationTrait;
/**
* Behat context class.
*/
class LaravelFeatureContext implements SnippetAcceptingContext
{
/**
* Responsible for providing a Laravel app instance.
*/
use ApplicationTrait;
/**
* Initializes context.
*
* Every scenario gets its own context object.
* You can also pass arbitrary arguments to the context constructor through behat.yml.
*/
public function __construct()
{
}
/**
* #BeforeScenario
*/
public function setUp()
{
if ( ! $this->app)
{
$this->refreshApplication();
}
}
/**
* Creates the application.
*
* #return \Symfony\Component\HttpKernel\HttpKernelInterface
*/
public function createApplication()
{
$unitTesting = true;
$testEnvironment = 'testing';
return require __DIR__.'/../../bootstrap/start.php';
}
/**
* #static
* #beforeSuite
*/
public function mockData()
{
$fakeDataRetriever = m::mock('My\Data\Api\Retriever');
$fakeData = [
'fake_name' => 'fake_value'
];
$fakeDataRetriever->shouldReceive('getData')->andReturn($fakeData);
$this->app->instance('My\Data\Api\Retriever', $fakeDataRetriever);
}
}

Related

Laravel Testing Request - Service Provider - Middleware Issue

I have a Laravel 5.5 App where I have a Service Provider which I use to put some stuff in the request->attributes to access it everywhere (simplified):
namespace App\Providers;
use App\Models\Domain;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;
class GlobalVarsServiceProvider extends ServiceProvider
{
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap the application services.
*
* #param Request $request
*
* #return void
*/
public function boot(Request $request)
{
$domain = .. get domain with language and some logic and cache because of multiple domains ..
$request->attributes->add(['domain' => $domain]);
}
}
I do this in a Service Provider, because then I can already use it in other Service Providers like my ViewComposerServiceProvider, where I compose some stuff for the Views. I'm able to access $domain everywhere like this:
$this->domain = $request->attributes->get('domain');
It works great. BUT not in testing. When I want to access $domain in a Unit Test in a middleware the $request->attributes are empty (In UnitTests as in DuskTests either).
It looks like the testing environment uses a different Request Lifecycle? If yes, what else is different in the testing environment?
What am I doing wrong?
-- Edit --
Test Example:
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
TestCase uses trait MakesHttpRequests which has method call. When you use get method in your tests, it's simply a shortcut to this.
In your test you can use it like this:
$this->call('GET', '/url/here', $yourRequestParametersHere);

Custom laravel migration command "[Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable"

I'm trying to create a custom laravel (5.2) migration command that basically works the same as migrate:status except it just lists the pending migrations instead of all the migrations.
To do this i've very simply copied the migrate:status into another class within my app/console directory and adjusted the code to suit my needs. However whenever I try to run it I get an error:
[Illuminate\Contracts\Container\BindingResolutionException]
Target [Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable while building [App\Console\Commands\PendingMigrations, Illuminate\Database\Migrations\Migrator].
The contents of the class itself and the fire() method doesn't seem to matter as it doesn't get that far, it fails within the __construct() method.
<?php namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Database\Migrations\Migrator;
class PendingMigrations extends Command
{
/**
* The console command name.
*
* #var string
*/
protected $name = 'migrate:pending';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Shows a list of pending migrations';
/**
* The migrator instance.
*
* #var \Illuminate\Database\Migrations\Migrator
*/
protected $migrator;
/**
* Create a new migration rollback command instance.
*
* #param \Illuminate\Database\Migrations\Migrator $migrator
* #return \Illuminate\Database\Console\Migrations\StatusCommand
*/
public function __construct(Migrator $migrator)
{
parent::__construct();
$this->migrator = $migrator;
}
/**
* Execute the console command.
*
* #return void
*/
public function fire()
{
}
}
The reason for it is likely to be something to do with the IoC container and the order with which things are loaded, but I don't know enough about the inner workings of Laravel to figure out any more than that.
It surely must be possible?
I am currently stuck on 5.2, so i'm not sure if this problem exists in more recent versions.
The only thing i've attempted so far is added the migration service provider to the top of the list in config/app.php however it didn't seem to have an affect and it was just a random guess anyway.
providers' => [
Illuminate\Database\MigrationServiceProvider::class,`
]
I got around this using:
$this->migrator = app('migrator');
but it is not necessarily the best way to do this
The Migrator instance is not bound to the class name in the IoC container, it is bound to the migrator alias.
From Illuminate\Database\MigrationServiceProvider:
/**
* Register the migrator service.
*
* #return void
*/
protected function registerMigrator()
{
// The migrator is responsible for actually running and rollback the migration
// files in the application. We'll pass in our database connection resolver
// so the migrator can resolve any of these connections when it needs to.
$this->app->singleton('migrator', function ($app) {
$repository = $app['migration.repository'];
return new Migrator($repository, $app['db'], $app['files']);
});
}
Since the class name is not bound in the IoC container, when Laravel resolves your command and attempts to resolve the Migrator dependency, it attempts to build a new one from scratch and fails because the Illuminate\Database\Migrations\MigrationRepositoryInterface is also not bound in the IoC container (hence the error you're receiving).
Since Laravel can't figure this out itself, you need to either register the binding for the Migrator class name, or you need to register the binding for your command. Laravel itself registers all the bindings for the commands in the Illuminate\Foundation\Providers\ArtisanServiceProvider. An example of the command.migrate binding:
/**
* Register the command.
*
* #return void
*/
protected function registerMigrateCommand()
{
$this->app->singleton('command.migrate', function ($app) {
return new MigrateCommand($app['migrator']);
});
}
So, in your AppServiceProvider, or another service provider you setup, you can add one of the following:
Register the command in the IoC:
$this->app->singleton(\App\Console\Commands\PendingMigrations::class, function ($app) {
return new \App\Console\Commands\PendingMigrations($app['migrator']);
});
Or, register the Migrator class name in the IoC:
$this->app->singleton(\Illuminate\Database\Migrations\Migrator::class, function ($app) {
return $app['migrator'];
});
As I don't want to register the migrator everywhere in the app, but I still want to extend the MigrateCommand itself, I came up with this approach to maintain my app as it is:
public function __construct()
{
app()->singleton(\App\Console\Commands\PendingMigrations::class, function ($app) {
return new \App\Console\Commands\PendingMigrations($app['migrator']);
});
parent::__construct(app('migrator'));
}

Validator event dispatched before Entity validation starts

Question
Is it possible in Symfony 2.8+ / 3.x+ to dispatch event before starting entity validation?
Situation:
Let's say we have 100 entities, they have #LifeCycleCallbacks, they have #postLoad Event that do something, but the result of this is only used for valiation of Entity, in 99% of situations result of #postLoad is unimportant for system. So if we have hundrets or thousands of Entities fetched from DB there will be a lot of machine-cycles lose for unimportant data.
It would be nice to run some kind of event, that will run method, that will populate that data for that specific Entity, just before validations starts.
instead of:
$entity->preValidate();
$validator = $this->get('validator');
$errors = $validator->validate($entity);
we could have:
$validator = $this->get('validator');
$errors = $validator->validate($entity);
And in validate() situation, preValidate() will be dispatched autmaticly as Event (of course with check if Entity does have such method).
CaseStudy:
I have a system that stores pages/subpages as entities. There can be 10 or 10000 pages/subpages
Pages/subpages can have file.
Entities stores only files names (becouse we can't store SplFileInfo - resource serialization restriction)
While Entity->file property is type of string, when I want to make it to be instance of File (so we can do validation of type File) I have something like:
/**
* #postLoad()
*/
public function postLoad()
{
//magicly we get $rootPath
$this->file = new File($rootPath . '/' . $this->file);
}
/**
* #prePersist()
* #preUpdate()
*/
public function preSave()
{
if ($this->file instance of File)
$this->file = $this->file->getFilename();
}
}
Ok, but postLoad() will CHANGE the property, and Doctrine will NOTICE that. So in next
$entityManager->flush()
ALL entities will be flushed - even if preSave() will change it back to be just string as it was before.
So if I have any other entity, let's say TextEntity, and I want to remove it
$entityManager->remove($textEntity);
$entityManager->flush();
All other Entities that are somehow changed (change was noticed by Doctrine), are flushed, no matter if value of file property is the same as in DB (and change was only temporary).
It will be flushed.
So we have hundrets, or thousends of pointless sql updates.
Btw.
1. ->flush($textEntity) will throw Exception, becouse ->remove($textEntity) have already "deleted" that entity.
2. Entity property ->file must be of type File for Assert/File, becouse FileValidator can only accept values of File or absolute-path-to-file.
But I will NOT store absolute-path-to-file, becouse it will be completly different on Dev, Stage, and Production environments.
This is problem that occured when I tried to make file uploading as it was described in Symfony cookbook http://symfony.com/doc/current/controller/upload_file.html.
My solution was to, in postLoad(), create File instance in property that is not Doctrine column, and is anoted to have assertion, etc.
That works, but the problem of useless postLoad()s stays, and i thought about events. That could be elastic, and very elegant solution - instead of controllers getting "fat".
Any one have better solution? Or know how to dispatch event if ->validate() happends?
Hello Voult,
Edit: first method is deprecated in symfony 3 as the thread op mentioned in a comment. Check the second method made for symfony 3.
Symfony 2.3+,Symfony < 3
What I do in this cases, since symfony and most other bundles are using parameters for service class definition, is to extend that service. Check the example below and for more information on extending services check this link
http://symfony.com/doc/current/bundles/override.html
First you need to add a some marker to your entities that require pre-validation. I usually use interfaces for stuff like this something like
namespace Your\Name\Space;
interface PreValidateInterface
{
public function preValidate();
}
After this you extend the validator service
<?php
namespace Your\Name\Space;
use Symfony\Component\Validator\Validator;
class MyValidator extends Validator //feel free to rename this to your own liking
{
/**
* #inheritdoc
*/
public function validate($value, $groups = null, $traverse = false, $deep = false)
{
if (is_object($value) && $value instanceof PreValidateInterface) {
$value->preValidate();
}
return parent::validate($value, $groups, $traverse, $deep);
}
}
And final step, you need to add the class value parameter to your 'parameters' config block in config.yml, something like this:
parameters:
validator.class: Your\Name\Space\MyValidator
This is the basic idea. Now you can mix end match this idea with whatever you want to achieve. For instance instead of calling a method on the entity (I usually like to keep business logic outside of my entities), you can look for the interface and if it is there you can launch a pre.validate event with that entity on it, and use a listener to do the job. After that you can keep the result from parent::validate and also launch a post.validate event. You see where i'm going with this. You basically can do whatever you like now inside that validate method.
PS: The example above is the easy method. If you want to go the event route, the service extension will be harder, since you need to inject the dispatcher into it. Check the link I provided at the beginning to see the other way to extend a service and let me know if you need help with this.
For Symfony 3.0 -> 3.1
In this case they managed to make it hard and dirtier to extend
Step 1:
Create your own validator something like this:
<?php
namespace Your\Name\Space;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Exception;
use Symfony\Component\Validator\MetadataInterface;
use Symfony\Component\Validator\Validator\ContextualValidatorInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class myValidator implements ValidatorInterface
{
/**
* #var ValidatorInterface
*/
protected $validator;
/**
* #param ValidatorInterface $validator
*/
public function __construct(ValidatorInterface $validator)
{
$this->validator = $validator;
}
/**
* Returns the metadata for the given value.
*
* #param mixed $value Some value
*
* #return MetadataInterface The metadata for the value
*
* #throws Exception\NoSuchMetadataException If no metadata exists for the given value
*/
public function getMetadataFor($value)
{
return $this->validator->getMetadataFor($value);
}
/**
* Returns whether the class is able to return metadata for the given value.
*
* #param mixed $value Some value
*
* #return bool Whether metadata can be returned for that value
*/
public function hasMetadataFor($value)
{
return $this->validator->hasMetadataFor($value);
}
/**
* Validates a value against a constraint or a list of constraints.
*
* If no constraint is passed, the constraint
* {#link \Symfony\Component\Validator\Constraints\Valid} is assumed.
*
* #param mixed $value The value to validate
* #param Constraint|Constraint[] $constraints The constraint(s) to validate
* against
* #param array|null $groups The validation groups to
* validate. If none is given,
* "Default" is assumed
*
* #return ConstraintViolationListInterface A list of constraint violations.
* If the list is empty, validation
* succeeded
*/
public function validate($value, $constraints = null, $groups = null)
{
//the code you are doing all of this for
if (is_object($value) && $value instanceof PreValidateInterface) {
$value->preValidate();
}
//End of code
return $this->validator->validate($value, $constraints, $groups);
}
/**
* Validates a property of an object against the constraints specified
* for this property.
*
* #param object $object The object
* #param string $propertyName The name of the validated property
* #param array|null $groups The validation groups to validate. If
* none is given, "Default" is assumed
*
* #return ConstraintViolationListInterface A list of constraint violations.
* If the list is empty, validation
* succeeded
*/
public function validateProperty($object, $propertyName, $groups = null)
{
$this->validator->validateProperty($object, $propertyName, $groups);
}
/**
* Validates a value against the constraints specified for an object's
* property.
*
* #param object|string $objectOrClass The object or its class name
* #param string $propertyName The name of the property
* #param mixed $value The value to validate against the
* property's constraints
* #param array|null $groups The validation groups to validate. If
* none is given, "Default" is assumed
*
* #return ConstraintViolationListInterface A list of constraint violations.
* If the list is empty, validation
* succeeded
*/
public function validatePropertyValue($objectOrClass, $propertyName, $value, $groups = null)
{
$this->validator->validatePropertyValue($objectOrClass, $propertyName, $value, $groups);
}
/**
* Starts a new validation context and returns a validator for that context.
*
* The returned validator collects all violations generated within its
* context. You can access these violations with the
* {#link ContextualValidatorInterface::getViolations()} method.
*
* #return ContextualValidatorInterface The validator for the new context
*/
public function startContext()
{
$this->validator->startContext();
}
/**
* Returns a validator in the given execution context.
*
* The returned validator adds all generated violations to the given
* context.
*
* #param ExecutionContextInterface $context The execution context
*
* #return ContextualValidatorInterface The validator for that context
*/
public function inContext(ExecutionContextInterface $context)
{
$this->validator->inContext($context);
}
}
Step 2:
Extend Symfony\Component\Validator\ValidatorBuilder something like this:
namespace Your\Name\Space;
use Symfony\Component\Validator\ValidatorBuilder;
class myValidatorBuilder extends ValidatorBuilder
{
public function getValidator()
{
$validator = parent::getValidator();
return new MyValidator($validator);
}
}
You need to override Symfony\Component\Validator\Validation. This is the ugly/dirty part, because this class is final so you can't extend it, and has no interface to implement, so you will have to pay attention to in on future versions of symfony in case backward compatibility is broken. It goes something like this:
namespace Your\Name\Space;
final class MyValidation
{
/**
* The Validator API provided by Symfony 2.4 and older.
*
* #deprecated use API_VERSION_2_5_BC instead.
*/
const API_VERSION_2_4 = 1;
/**
* The Validator API provided by Symfony 2.5 and newer.
*/
const API_VERSION_2_5 = 2;
/**
* The Validator API provided by Symfony 2.5 and newer with a backwards
* compatibility layer for 2.4 and older.
*/
const API_VERSION_2_5_BC = 3;
/**
* Creates a new validator.
*
* If you want to configure the validator, use
* {#link createValidatorBuilder()} instead.
*
* #return ValidatorInterface The new validator.
*/
public static function createValidator()
{
return self::createValidatorBuilder()->getValidator();
}
/**
* Creates a configurable builder for validator objects.
*
* #return ValidatorBuilderInterface The new builder.
*/
public static function createValidatorBuilder()
{
return new MyValidatorBuilder();
}
/**
* This class cannot be instantiated.
*/
private function __construct()
{
}
}
And last step overwrite the parameter validator.builder.factory.class in your config.yml:
parameters:
validator.builder.factory.class: Your\Name\Space\MyValidation
This is the least invasive way to do it, that i can find. Is not that clean and it could need some maintaining when you upgrade symfony to future versions.
Hope this helps, and happy coding
Alexandru Cosoi

How to extend the Symfony2 Debug Toolbar with custom data?

I want to extend the Symfony2 Debug Toolbar with my own custom data.
I have a service where I want to log specific method calls and then display them in the web debug toolbar.
I read the cookbook article, but it's not very helpful.
I created my own DataCollector class:
class PermissionDataCollector extends DataCollector
{
private $permissionCalls = array();
private $permissionExtension;
public function __construct(PermissionExtension $permissionExtension)
{
$this->permissionExtension = $permissionExtension;
}
/**
* Collects data for the given Request and Response.
*
* #param Request $request A Request instance
* #param Response $response A Response instance
* #param \Exception $exception An Exception instance
*
* #api
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->permissionCalls = $this->permissionExtension->getPermissionCalls();
$this->data = array(
'calls' => $this->permissionCalls
);
}
public function getPermissionCallsCount()
{
return count($this->permissionCalls);
}
public function getFailedPermissionCallsCount()
{
return count(array_filter($this->permissionCalls, array(&$this, "filterForFailedPermissionCalls")));
}
private function filterForFailedPermissionCalls($var)
{
return $var['success'];
}
/**
* Returns the name of the collector.
*
* #return string The collector name
*
* #api
*/
public function getName()
{
return 'permission';
}
}
The PermissionExtension logs all calls and then I want to retrieve this array of calls in
PermissionDataCollector.
And a template just outputting {{ collector.permissionCallsCount }}.
The section gets displayed in the in the toolbar but it just shows a 0 which is wrong.
I'm not sure if I'm even doing this right, because the documentation lacks this section. I'm using Symfony 2.1
Has anybody extended the toolbar with custom data?
ah great! It works. I basically need to refer to $this->data all the time.
The reason for this that ->data is used by the Symfony\Component\HttpKernel\DataCollector\DataCollector and serialized (see DataCollector::serialize).
This is later stored (somehow, I don't know where, but it is later unserialized). If you use own properties the DataCollector::unserialize just prunes your data.
See https://symfony.com/doc/current/profiler/data_collector.html#creating-a-custom-data-collector
As the profiler serializes data collector instances, you should not store objects that cannot be serialized (like PDO objects) or you need to provide your own serialize() method.
Just use $this->data all the time, or implement your own \Serializable serializing.

CodeIgniter: MVC and Widgets?

I'm new to codeigniter and building web applications using MVC. I'm trying to wrap my head around how I would implement widgets in a modular fashion in my application. My question is more theoretical at this point. I don't have actual code to show.
What I want to know is this, how would I construct a data-driven widget in such a way that I can simply drop it on to any page that I want. For example, let's say I have a widget called Widget. I've created a model file called /models/widget_model.php. I then have a controller file called /controllers/widget.php. Obviously my controller will use the model to grab necessary data from my database. What I don't understand is how to use this as a widget dropped onto multiple views. What I'm seeing and understand so far is how to use a controller to drive a specific view. So it's basically like one controller is used per page. What would be the process of using this widget in a modular fashion I guess?
What you search for is HMVC. There are two common library/packages you can use : Modular CI or HMVC. With that, you can actually put something like <?php echo Modules::run('module/controller/method', $param, $...); ?> as a widget, in your view files.
You can do it via drivers. Send the controller as an object reference to the driver to use view class. Then you just load drivers and use them as plugins.
Edit:
Here is the code I use in my application:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* CodeIgniter base widget driver
*
* #author Alex
* #version 1.0.0
*/
class Basedriver {
/**
* Current specified controller.
* #var CI_Controller
*/
public $controller;
/**
* Contents of the driver which should be outputted or returned.
* #var string
*/
protected $contents;
/**
* Loader Class
* #var CI_Loader
*/
protected $load;
/**
* Constructor function for Basedriver class
*/
public function __construct()
{
$this->controller =& get_instance();
$this->load = $this->controller->load;
}
/**
* Renders driver data into specified output. If $echo_contents is true,
* output is echoed to the client, otherwise it is returned.
* #param boolean $echo_contents Specifies whether the content should be outputted or returned as string
* #param mixed $params Array of parameters which should be sent to the driver
* #return string Returned driver data if $echo_contents is set
*/
public function render($params = NULL, $echo_contents = true)
{
$this->parse_params($params);
$this->run();
if ($echo_contents)
echo $this->contents;
else
return $this->contents;
return NULL;
}
/**
* Default run function for all drivers, should be overidden by extending classes.
*/
protected function run()
{
$this->contents = NULL;
}
/**
* Parses parameters and sets them as variables.
* Default variables need to be defined in extending class
*/
protected function parse_params($params)
{
if ($params === NULL) return;
foreach($params as $variable => $value)
{
if (isset($this->$variable))
$this->$variable = $value;
}
}
}
/* End of file Basedriver.php */
/* Location: ./application/libraries/Basedriver.php */
Load class is there to allow you to use view class and controller is there to allow you to use database functions and to give you some other access if you need it. This class needs to be loaded before all other drivers (widgets) and all drivers (widgets) need to extend this class. You can do this by adding 'basedriver' in $config['libraries'] array in application/config/autoload.php.
Example Driver Widget:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Example extends Basedriver
{
protected $parameter1 = 'defaultvalueparam1';
protected $parameter2 = 'defaultvalueparam2';
protected function run()
{
// Widget logic here...
// you can use $this->load->view and $this->controller->db here
$this->contents = 'final_processed_data_here';
}
}
/* End of file Example.php */
/* Location: ./application/libraries/Example/Example.php */
To use the driver which extends Basedriver as a widget, example:
$this->load->driver('example');
$this->example->render(array('parameter1' => '1', 'parameter2' => '2'));
I think you could simply using CI's view system. You create a view per widget, then you inject any variable you want from your model, and finally you display the resulting HTML anywhere you want. I can't think of any particular difficulty.

Resources