phpunit mock method called in constructor - constructor

I test a config class, which is parsing a config file and allows me to get the various settings for an app.
My goal is to mock the parse() method of the Config class, which is called in the constructor and to set what this method is returning in the constructor.
This way, it prevents file_get_contents() from being called (in the parse() method) and enables me to have a Config class with the config property already set to contain an array of properties.
But I haven't succeeded doing that.
Here is the code:
The config class:
<?php namespace Example;
use Symfony\Component\Yaml\Parser;
class Config
{
private $parser;
private $config;
public function __construct(Parser $parser, $filePath)
{
$this->parser = $parser;
$this->config = $this->parse($filePath);
}
public function parse($filePath)
{
$fileAsString = file_get_contents($filePath);
if (false === $fileAsString) {
throw new \Exception('Cannot get config file.');
}
return $this->parser->parse($fileAsString);
}
public function get($path = null)
{
if ($path) {
$config = $this->config;
$path = explode('/', $path);
foreach ($path as $bit) {
if (isset($config[$bit])) {
$config = $config[$bit];
}
}
return $config;
}
return false;
}
}
The test:
<?php namespace Example;
class ConfigTest extends \PHPUnit_Framework_TestCase
{
private function getConfigTestMock($configAsArray)
{
$parser = $this->getMockBuilder('\Symfony\Component\Yaml\Parser')
->getMock();
$configMock = $this->getMockBuilder('Example\Config')
->setConstructorArgs([$parser, $configAsArray])
->setMethods(['parse', 'get'])
->getMock();
$configMock->expects($this->once())
->method('parse')
->willReturn($configAsArray);
return $configMock;
}
/**
* #test
*/
public function get_returns_false_if_no_path_given()
{
$configMock = $this->getConfigTestMock(['param1' => 'value1']);
// Testing further...
}
}

I suggest you to make a functional test mocking the interaction with the file system, without do partial mocking of the tested class.
I recently discover the vfsStream library used in a great article of William Durand about Symfony2 and DDD.
So you can install this library in your composer.json (I tested the solution with the 1.4 version) and try this example test class:
<?php
namespace Acme\DemoBundle\Tests;
use Acme\DemoBundle\Example\Config;
use org\bovigo\vfs\vfsStream;
use Symfony\Component\Yaml\Parser;
class ConfigTest extends \PHPUnit_Framework_TestCase
{
/**
* #test
*/
public function valid_content()
{
$content = "param1: value1";
$root = vfsStream::setup();
$file = vfsStream::newFile('example.txt')
->withContent($content)
->at($root);
$filepath = $file->url();
$parser = new Parser();
$config = new Config($parser, $filepath);
$this->assertEquals("value1", $config->get("param1"));
}
}
Hope this help

For test the Config class you need to mock only the Parser and use the real Config class. As Example:
<?php
namespace Acme\DemoBundle\Tests;
use Acme\DemoBundle\Example\Config;
class ConfigTest extends \PHPUnit_Framework_TestCase
{
private function getConfigTestMock($configAsArray)
{
$parser = $this->getMockBuilder('\Symfony\Component\Yaml\Parser')
->getMock();
$parser->expects($this->once())
->method('parse')
->willReturn($configAsArray);
$configMock = new Config($parser,"fakePath");
return $configMock;
}
/**
* #test
*/
public function get_returns_false_if_no_path_given()
{
$configMock = $this->getConfigTestMock(['param1' => 'value1']);
$this->assertEquals("value1",$configMock->get("param1"));
// Testing further...
}
}
Hope this help

Related

Using same class object in another function

My Code is Like This
public function function1(){
$client = new Google_Client();
$client->setAccessToken('Some value');
}
public funnction function2(){
// Here I want to get the token using $client->getAccessToken()
}
How can use the same instance of class from function1 in function2? So I can use $client->getAccessToken() in function2
Simple Solution use global
<?php
$client;
function function1(){
global $client;
$client = new Google_Client();
$client->setAccessToken('Some value');
}
function function2(){
global $client;
$token=$client->getAccessToken();
}
?>
It's simple but not recommended. It's never a good Idea to use a global variables, we must avoid it as long as possible.
So there must be some other way,right ?
well yes use class
<?php
class ForClient {
private $client;
function __construct(){
$client = new Google_Client();
}
public function setToken($val){
$client->setAccessToken($val);
}
public function getToken(){
$token=$client->getAccessToken();
return $token;
}
}
$token_handler = new ForClient();
$token_handler->setToken(25);
$token=$token_handler->getToken();
?>
I changed the function names to make code more readable.

Controller not defined laravel

the error :
Action App\Http\Controllers\formController#form not defined. (View: C:\xampp\htdocs\ucar3\resources\views\layouts\Form.blade.php) (View: C:\xampp\htdocs\ucar3\resources\views\layouts\Form.blade.php)
I tried changing the route in web.php
web.php
Route::resource('Inscription','inscriController');
Controller
class FormController extends Controller
{
public function show()
{
return view('pages.Inscription');
}
public function insert(Request $request)
{
$Cin = $request->input('Cin');
$nom = $request->input('nom');
$prenom = $request->input('prenom');
$email = $request->input('email');
$telephone = $request->input('telephone');
$specialite = $request->input('specialite');
$typedediplome = $request->input('typedediplome');
$mentiondiplome = $request->input('mentiondiplome');
$redoublement = $request->input('redoublement');
$communication = $request->input('communication');
$publication = $request->input('publication');
$experiencePedagogiqueSecondaire = $request
->input('experiencePedagogiqueSecondaire');
$experiencePedagogiqueSupérieur = $request
->input('experiencePedagogiqueSupérieur');
$data = array(['Cin'=>$Cin,
'nom'=>$nom,
'prenom'=>$prenom,
'email'=>$email,
'telephone'=>$telephone,
'specialite'=>$specialite,
'typedediplome'=>$typedediplome,
'mentiondiplome'=>$mentiondiplome,
'redoublement'=>$redoublement,
'communication'=>$communication,
'publication'=>$publication,
'experiencePedagogiqueSecondaire'=>$experiencePedagogiqueSecondaire,
'experiencePedagogiqueSupérieur'=>$experiencePedagogiqueSupérieur
]);
DB::table('users')->insert($data);
return view('pages.success');
}
}
Model
class form extends Model
{
public $table = "form";
protected $fillable = [
'Cin',
'nom',
'prenom',
'telephone',
'email',
'specialite',
'typedediplome',
'mentiondiplome',
'redoublement',
'communication',
'publication',
'experiencePedagogiqueSecondaire',
'experiencePedagogiqueSupérieur'
];
public $timestamps = true;
}
As the Error says
formController#form not defined.
but in your class you've
FormController extends Controller
Please check if you are calling FormController with lower case 'F'.
I think you have problems with your inscriController and your routes, use the following code:
web.php
use App\Http\Controllers\inscriController;
Route::resource('Inscription', inscriController::class);
app/Http/Controllers.php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
class inscriController extends Controller {
public function __construct() {
$this->middleware('auth');
}
}
Check if you set the correct namespace in the FormController.php
You are also missing a function form inside your FormController.

How can I use same code in different functions in Symfony Controller?

In my Controller I am using several functions. In this functions I am using similar code.
So I am wondering if there is a possibility outsource this code to not have to write it repeatedly. If this is possible, what would be the best way to do it?
class PagesController extends AbstractController
{
/**
* #Route("/documents/{slug}", name="documents", methods={"GET","POST"})
*/
public function documents($slug, Request $request)
{
$page = $this->getDoctrine()->getRepository(Pages::class)->findOneBy(['slug'=>$slug]);
$entityManager = $this->getDoctrine()->getManager();
$cmf = $entityManager->getMetadataFactory();
$classes = $cmf->getMetadataFor($relation_name);
$fieldMappings = $classes->fieldMappings;
$associationMappings = $classes->associationMappings;
$fields = (object)array_merge((array)$fieldMappings, (array)$associationMappings);
}
/**
* #Route("/blog/{slug}", name="single", methods={"GET","POST"})
*/
public function blog($slug, Request $request)
{
$page = $this->getDoctrine()->getRepository(Pages::class)->findOneBy(['slug'=>$slug]);
$entityManager = $this->getDoctrine()->getManager();
$cmf = $entityManager->getMetadataFactory();
$classes = $cmf->getMetadataFor($relation_name);
$fieldMappings = $classes->fieldMappings;
$associationMappings = $classes->associationMappings;
$fields = (object)array_merge((array)$fieldMappings, (array)$associationMappings);
}
/**
* #Route("/contact/{slug}", name="contact", methods={"POST", "GET"})
*/
public function contact($slug, Request $request)
{
$page = $this->getDoctrine()->getRepository(Pages::class)->findOneBy(['slug'=>$slug]);
$entityManager = $this->getDoctrine()->getManager();
$cmf = $entityManager->getMetadataFactory();
$classes = $cmf->getMetadataFor($relation_name);
$fieldMappings = $classes->fieldMappings;
$associationMappings = $classes->associationMappings;
$fields = (object)array_merge((array)$fieldMappings, (array)$associationMappings);
}
}
You can use private method and call it, but in your case you could use Page typehint right in the parameter:
/**
* #Route("/contact/{slug}", name="contact", methods={"POST", "GET"})
*/
public function contact(Page $slug, Request $request)
The keyword here is services. Move your business logic to a other classes and auto-inject it in your controller using autowiring. This is a Symfony Best Practice:
Symfony follows the philosophy of "thin controllers and fat models".
This means that controllers should hold just the thin layer of
glue-code needed to coordinate the different parts of the application.
You should read about these best practices!
You can inject services in your controller class and in a specific action:
class PagesController extends AbstractController
{
public function __construct(Rot13Transformer $transformer)
{
$this->transformer = $transformer;
}
/**
* #Route("/documents/{slug}", name="documents", methods={"GET","POST"})
*/
public function documents($slug, Request $request, PagesRepository $repo)
{
$page = $repo->findOneBy(['slug'=>$slug]);
$foo = $repo->doSomethingDifferentWithEntities($page)
$bar = $this->transformer->transform($foo);
}
}
#Jarla Additionally to #Stephan Vierkant answer you can use #ParamConverter annotation
In your case, it will be:
/**
* #Route("/documents/{slug}", name="documents", methods={"GET","POST"})
* #ParamConverter("page", options={"mapping": {"slug": "slug"}})
*/
public function documents(Page $page, Request $request)
{
$foo = $repo->doSomethingDifferentWithEntities($page)
$bar = $this->transformer->transform($foo);
}

How to implement common method in php?

How to implement a method in a class that can be used by everyone users in php ?
My idea is not to repeat the implementation of readsales in each of the entities
i have 3 users:
admin readSales function,
manager readSales function,
employee, insertSale AND readSales function
Is it possible to implement common methods in a single class? And then call each of the methods to the daughter classes?
abstract class commonMethods {
abstract readSales() {
$pdo = new PDO();
//statements
//readSales
//return $list;
}
}
class Manager extends commonMethods {
function readSales(){
return readSales();
}
}
Yes this is possible, what you need is either a trait or an abstract class.
Here's an example with Abstract class :
<?php
/**
* The abstract class
*/
abstract class CommonMethods
{
public function readSales(){
// Your code
}
public function hello(){
echo "Hello";
}
}
/**
* The Class
*/
class Manager extends CommonMethods
{
// No need to add the "readSales" or "hello" method
// Since we had extended them
public function world(){
echo "World";
}
}
$Manager = new Manager;
$Manager->readSales(); // Works!
$Manager->hello(); // Output: "Hello"
$Manager->world(); // Output: "World"
// The final output: "HelloWorld"
Reference: http://php.net/manual/en/language.oop5.abstract.php

Is it possible for PhpStorm to infer the return type of this method?

I imagine I might need to add a special annotation somewhere, but I'm crossing my fingers that PhpStorm is smart enough to resolve return types given awkward inheritance patterns.
For example, I have some code that resembles this:
<?php
class Collection extends \ArrayObject
{
public function __construct(array $items)
{
foreach ($items as $key => $value) {
if (isset(static::$requiredType) && !$item instanceof static::$requiredType)
$this->offsetSet($key, $value);
}
}
public function getFirst()
{
return $this->offsetGet(0);
}
}
class MessageCollection extends Collection
{
protected static $requiredType = 'Message';
}
class UserCollection extends Collection
{
protected static $requiredType = 'User';
}
I'd like it if when I call UserCollection::getFirst() it inferred that a User was returned, while when I call MessageCollection::getFirst() it inferred that a Message was returned. Is there some annotation I could put in somewhere to achieve this result?
My first thought was something like this:
/**
* #return Message|User|XXXX|YYYY|ZZZZ|AAAA|BBBB|CCCC|DDDD
*/
public function getFirst()
{
return $this->offsetGet(0);
}
but I imagine that would get a little ridiculous to the point of being useless as I add more collection classes.
Try this:
/**
* #method \User getFirst()
*/
class UserCollection extends Collection
{
protected static $requiredType = 'User';
}