how to write mock test in laravel (IOC) - php

i dont know how to write mock test for my service
here is my compressContract Interface
interface compressContract
{
public function compress();
}
here is my ZipCompress class
<?php
namespace App\Http\Controllers\My\compression;
class ZipCompress implements compressContract
{
public function compress()
{
var_dump('im zip compression');
}
}
and here is my compressManager class
class compressManager
{
public compressContract $compressContract;
public function __construct(compressContract $compressContract)
{
$this->compressContract = $compressContract;
}
public function compressFile(Request $request)
{
//do somthing
}
}
and here is AppServiceProvider class in the boot method
public function boot()
{
$this->app->bind(CompressContract::class , ZipCompress::class);
}
i read articles about mocking a class but i cant write mock for these class

Firstly basics, class names are pascal cased. compressContract should be CompressContract.
A unit test, mocking the compression contract would look like this. Laravel has helpers in the test cases to help you actual bind the mocked instance to the container. This is described in the documentation here.
/** #test **/
public function your_test()
{
$this->mock(CompressContract::class, function (MockInterface $mock) {
$mock->shouldReceive('compress')->once()->andReturn('If you want correct return');
});
// call your class or http test your controller.
}

Related

Automatically load modules or libraries in Laravel controller or model

In my Laravel project, many Controllers and Models have the same headers.
For example, they all include
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use DB;
So, each time I ever create a new Controller, I have to insert the same header part. (like above)
Is there any way to autoload the above libraries in all controllers and models?
You could create a base class which accepts the dependencies in the constructor.
You could also create a Container class which reduces the amount of direct dependencies you have in a controller :
class Container
{
public function __construct(/* Your dependencies */) {
/* Set dependencies */
}
/* Dependency getters */
}
Controller:
class MyController
{
public function __construct(Container $container)
{
$this->container = $container;
}
public function index() {
/* Access dependencies on container */
}
}
But to be honest if you depend on request in a model class you have done something wrong. Typically in MVC the request info is passed from within the controller to the model, so the model does not know about the Request object, the values from Request are passed through as primitive values or value objects.
Update to explain my answer
You can reduce the amount of dependencies in your model classes (which is the best option) by simply following these rules, as explained here: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
So for example instead of writing:
//Note this is not a laravel specific example
class MyModel
{
private $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function getInfo()
{
return /* find info on $request->get('id'); */
}
}
You can write:
class MyController
{
public function __construct(MyModel $myModel)
{
$this->myModel = $myModel;
}
public function index()
{
$info = $this->myModel->getInfo($this->getRequest()->get('id'));
}
}
Where Request dependency is now removed from the Model:
//Note this is not a laravel specific example
class MyModel
{
public function getInfo(int $id)
{
return /* find info on $id; */
}
}
This was the simplest explanation, it will get more complex, so the best idea is to read and understand the article.

How to correctly extend and use other interfaces?

I'm trying to make use of a base interface for all my other interfaces as follows:
Base interface
<?php
namespace App\Repositories\Data;
interface IDataRepository
{
public function getAll();
public function getById($id);
public function create($model);
public function update($model);
public function delete($id);
}
Implemented base interface
<?php namespace App\Repositories\Data;
use Illuminate\Database\Eloquent\Model;
class DataRepository implements IDataRepository
{
// model property on class instances
protected $model;
// Constructor to bind model to repo
public function __construct(Model $model)
{
$this->model = $model;
}
// Get all instances of model
public function getAll()
{
return $this->model->all();
}
// create a new record in the database
public function create($model)
{
return $this->model->create($model);
}
// update record in the database
public function update($model)
{
$record = $this->find($model.id);
return $record->update($model);
}
// remove record from the database
public function delete($id)
{
return $this->model->destroy($id);
}
// show the record with the given id
public function getById($id)
{
return $this->model-findOrFail($id);
}
}
The interface where i'm trying to make use of the base interface
<?php
namespace App\Repositories;
use App\Repositories\Data\IDataRepository;
interface ITestRepository extends IDataRepository
{
}
implementation
<?php namespace App\Repositories;
use App\Library\Classes\Test;
use Illuminate\Database\Eloquent\Model;
class TestRepository implements ITestRepository
{
}
In my controller i'm trying to just call test repository so i can use all the base repository functions:
class TestController extends Controller
{
protected $testRepository;
public function __construct(Test $test)
{
$this->testRepository = new TestRepository($test);
}
public function index()
{
$data['testData'] = $this->testRepository->getAll();
return view('test', $data);
}
}
But i get the following error:
Class App\Repositories\TestRepository contains 5 abstract methods and
must therefore be declared abstract or implement the remaining methods
My application works fine if i only make use of my base interface and pass through a model. What would be the correct way to share functions from my base interface across all my other interfaces, so as to prevent code duplication? I appreciate any help.
I think that a Trait which will contains all methods of your interface declaration is the best choice. Something like (not sure about logic):
namespace App\Repositories;
trait TDataRepository
{
// model property on class instances
protected $model;
// Constructor to bind model to repo
public function __construct(Model $model)
{
$this->model = $model;
}
// Get all instances of model
public function getAll()
{
return $this->model->all();
}
// create a new record in the database
public function create($model)
{
return $this->model->create($model);
}
// update record in the database
public function update($model)
{
$record = $this->find($model.id);
return $record->update($model);
}
// remove record from the database
public function delete($id)
{
return $this->model->destroy($id);
}
// show the record with the given id
public function getById($id)
{
return $this->model-findOrFail($id);
}
}
And then just use it for classes with base interface:
namespace App\Repositories;
use App\Library\Classes\Test;
use Illuminate\Database\Eloquent\Model;
class TestRepository implements ITestRepository
{
use TDataRepository;
}
Also there are some other options:
abstract class with methods for base interface but it not so flexible like trait,
composition but you should change base idea and create a new entity for composition.
<?php
namespace App\Repositories;
use App\Interfaces\ITestRepository;
class TestRepository implements ITestRepository
{
public function getAll()
{
// TODO: Implement getAll() method.
}
public function getById($id)
{
// TODO: Implement getById() method.
}
public function create($model)
{
// TODO: Implement create() method.
}
public function update($model)
{
// TODO: Implement update() method.
}
public function delete($id)
{
// TODO: Implement delete() method.
}
}
Class must be declared abstract or implement methods 'getAll', 'getById', 'update', 'create', 'delete'
So All the method is by default abstract method in interface and you have to define all method in this class.
The class TestRepository should not implement any interface, but extend DataRepository:
<?php namespace App\Repositories;
use App\Repositories\Data\DataRepository;
class TestRepository extends DataRepository
{
}
DataRepository contains already the implementation of the interface IDataRepository. When you create a class implementing ITestRepository you will have to define the implementation of all the methods in the interface (which are the same as the base interface, in your case).

Can`t instantiate class using IOC contatiner laravel

I`m trying to get instanse of my class using dependency injection.
This class has own service provider that registered in app.php
class Something
{
private $variable;
public function __construct(string $variable)
{
$this->variable = $variable;
}
}
this is service provider
class SomethingServiceProvider extends ServiceProvider
{
public function boot()
{
}
public function register()
{
$this->app->singleton('Something', function () {
return new Something( 'test');
});
}
}
and when I try to use this class instance in controller...
class TestController extends AppBaseController
{
public function __construct(Something $something)
{
$this->something = $something;
}
...
I got error:
"Unresolvable dependency resolving [Parameter #0 [ string $variable ]]
in class Something at
Container->unresolvablePrimitive(object(ReflectionParameter)) in
Container.php (line 848) "
I guess YourServiceProvider::__construct accepts a non-typed $app instance. This means that Laravel cannot automatically resolve it. Try typing it; public function __construct(Application $app) with the proper use-statement.
More: https://laravel.com/docs/5.3/container#automatic-injection
When you register something to be injected you need to use the fully qualified class name:
public function register()
{
$this->app->singleton(Something::class, function () {
return new Something( 'test');
});
}
Otherwise Laravel will try to automatically inject something which implies that it will try to inject the dependencies of Something first and then determine that this is a string and fail.

How to Auto Inject Dependency with Laravel

I'm trying to build a structure that uses dependency injection on lumen.
I Have a Service Layer and repository layer.
I want to inject the repository layer to service layer. Let me try to show you the code
interface IUserRepostitory {
public function getByID($id);
}
class UserRepository extends BaseRepository implements IRepository{
public function getByID($id) {
//Please don't think how this function works, my question about dependency injection
return $this->findOrFail($id);
}
}
interface IService {
public function getByID($id);
}
class UserService implements IService{
private $Repository;
public __construct(IUserRepositor $UserRepository) {
$this->Repository = $UserRepository
}
public function getByID($id) {
return $this->Repository->getByID($id);
}
}
Here I'm registering the dependency resolver.
//Dependency resolver for Repository Layer
class RepositoryServiceProvider extends ServiceProvider {
public function register()
{
$this->app->singleton(IUserRepository::class, function () {
return new UserRepository();
});
}
}
Here I'm registering Service Layer
class ServiceServiceProvider extends ServiceProvider {
public function register()
{
$this->app->singleton(IUserService::class, function () {
//Here is what I don't like
//It would be great a solution that automaticly resolve UserRepository.
return new UserService(new UserRepository());
});
}
}
As you see, I want to auto resolve the dependency into UserService. But singleton method need to create the returning object.
Is there a better way for this?
*** note : please don't pay attention on syntax, I'm writing it on lumen but the same problem on laravel.
Once you bind the UserRepository to the IUserRepository, you may then instantiate the IUserService with the IUserRepository by resolving with the make function!
Modifying your ServiceServiceProvider as such:
class ServiceServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(IUserService::class, function ($app) {
return new UserService($app->make(IUserRepository::class));
});
}
}

Issue with Facade and injected dependency in Laravel 4

I am having an issue getting a Facade to work properly with a dependency injected into the underlying class.
I have a class called 'Listing'. It has one dependency called 'AdvertRepository' which is an interface and a class called EloquentAdvert which implements the interface. The code for these three classes is here:
// PlaneSaleing\Providers\Listing.php
<?php namespace PlaneSaleing\Providers;
use PlaneSaleing\Repositories\Advert\AdvertRepository;
class Listing {
protected $advert;
public function __construct (AdvertRepository $advert_repository) {
$this->advert = $advert_repository;
}
public function test() {
$this->advert->test();
}
public function test2() {
echo "this has worked";
}
}
// PlaneSaleing\Repositories\Advert\AdvertRepository.php
<?php namespace PlaneSaleing\Repositories\Advert;
interface AdvertRepository {
public function test();
}
// PlaneSaleing\Repositories\Advert\EloquentAdvert.php;
<?php namespace PlaneSaleing\Repositories\Advert;
class EloquentAdvert implements AdvertRepository {
public function test() {
echo 'this has worked';
}
}
I have then created a service provider called ListingServiceProvider.php, which has the following code:
// PlaneSaleing/Providers/ListingServiceProvider.php
<?php namespace PlaneSaleing\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\App;
class ListingServiceProvider extends ServiceProvider {
public function register() {
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', 'PlaneSaleing\Repositories\Advert\EloquentAdvert');
}
}
I also added this to the ServiceProviders array in app.php
Now, if I inject Listing as a dependency into a controller and call the test method (as shown below) Laravel correctly detects the dependency, instantiates EloquentAdvert via its binding and displays 'this has worked'.
// Controllers/TestController.php
use PlaneSaleing\Providers\Listing;
class TestController extends BaseController {
protected $listing;
public function __construct(Listing $listing) {
$this->listing = $listing;
}
public function test1() {
$this->listing->test();
}
}
Now, I then created a facade for Listing. I added a new facade as follows and added an alias in app.php:
// PlaneSaleing\Providers\ListingFacade.php
<?php namespace PlaneSaleing\Providers;
use Illuminate\Support\Facades\Facade;
class ListingFacade extends Facade {
protected static function getFacadeAccessor() {
return 'Listing';
}
}
I also added the following new lines to ListingServiceProvider.php:
<?php namespace PlaneSaleing\Providers;
use PlaneSaleing\Repositories\Advert\AdvertRepository;
use PlaneSaleing\Repositories\Advert\EloquentAdvert;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\App;
class ListingServiceProvider extends ServiceProvider {
public function register() {
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', 'PlaneSaleing\Repositories\Advert\EloquentAdvert');
// New lines...
$this->app['Listing'] = $this->app->share(function() {
return new Listing(new AdvertRepository);
});
}
}
NOW...if I call Listing::test(), I get the following error: Cannot instantiate interface PlaneSaleing\Repositories\Advert\AdvertRepository.
If I call Listing::test2() , I get 'this has worked' so it seems the Facade is working correctly.
It seems that when accessing Listing via its Facade the binding between AdvertRepository and EloquentAdvert doesnt work. I have looked at my code in the ServiceProvider thinking it was the issue, but I cant figure it out.
Both the Facade and binding work when tested individually but not when both are used at the same time.
Any ideas???
OK, So I have figured it out...For those who run into a similar problem...
The offending statement was in ListingServiceProvider.php which read:
$this->app['Listing'] = $this->app->share(function() {
return new Listing(new AdvertRepository);
});
The error is the new AdvertRepository statement. The reason being is that, we are telling php to directly instantiate the interface 'AdvertRepository'. Instead, we need to tell Laravel to instantiate the appropriate implementation of the 'AdvertRepository' interface. To do that, we use App::make('AdvertRepository'). That way, Laravel uses the binding previously declared to instantiate the correct implementation.
If your constructor is not being inject with a class, you must tell Laravel what class will be used when it needs to instantiate a particular interface:
Put this in your filters or bindings file:
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', function()
{
return new PlaneSaleing\Repositories\Advert\EloquentAdvert;
});

Categories