Mockery "shouldReceive()->once()" throwing "called exactly 1 times" error - php

I am about $this->close() to giving up on Mockery in my unit tests. Here's what's going on, I am working with Laravel 5.1 and I'm trying to test my repository pattern abstraction using Mockery in PHPUnit. I've followed the tutorials, poured over the StackOverflow questions so it's not a duplicate. When you see anything about modules here, it's PingPong Sky Modules package.
Basically, when I try to mock the repository interface and set shouldReceive('create')->with([])->once() , Mockery throws:
Mockery\Exception\InvalidCountException: Method create(array()) from Mockery_0_Modules_Documents_Repositories_DocumentRepositoryInterface should be called exactly 1 times but called 0 times.
DocumentsTest.php
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
class FileUploadTest extends TestCase {
use WithoutMiddleware;
public function mock($class){
$mock = Mockery::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
public function testFileUpload(){
// Mock the Repository
$mock = $this->mock('\Modules\Documents\Repositories\DocumentRepositoryInterface');
$mock->shouldReceive('create')->with([])->once();
$this->call('POST', '/documents', [], [], []);
$this->assertResponseStatus(201);
}
}
DocumentRepositoryInterface.php
<?php namespace Modules\Documents\Repositories;
interface DocumentRepositoryInterface {
public function create(array $data);
}
DatabaseDocumentRepository.php
<?php namespace Modules\Documents\Repositories;
use Modules\Documents\Repositories\DocumentRepositoryInterface;
use \Illuminate\Database\Eloquent\Model;
class DatabaseDocumentRepository implements DocumentRepositoryInterface {
protected $documents;
public function __construct(Model $documents) {
$this->documents = $documents;
}
public function create(array $data) {
// Eloquent code.
return "response";
}
}
Document.php
<?php namespace Modules\Documents\Entities;
use Illuminate\Database\Eloquent\Model;
class Document extends Model {
protected $fillable = [];
}
routes.php
$this->app->bind(
'Modules\Documents\Repositories\DocumentRepositoryInterface', function(){
return new Modules\Documents\Repositories\DatabaseDocumentRepository(new Modules\Documents\Entities\Document());
});
Route::group(['prefix' => 'documents', 'namespace' => 'Modules\Documents\Http\Controllers'], function(){
Route::post('/', ['as' => '/', 'uses'=> 'DocumentsController#create']);
});
DocumentsController.php
<?php namespace Modules\Documents\Http\Controllers;
use Modules\Documents\Repositories\DocumentRepositoryInterface;
use Pingpong\Modules\Routing\Controller;
use Module;
use Illuminate\Http\Response;
use Illuminate\Http\Request;
class DocumentsController extends Controller {
private $documents;
public function __construct(DocumentRepositoryInterface $doc){
$this->documents = $doc;
}
public function create(Request $request){
$this->documents->create([]);
return response("", Response::HTTP_CREATED);
}
}
I want to say it has something to do with the mocked object not getting injected into the DocumentsController because the create() function is getting called. I put a print_r in the create function and it displayed in my console. This is strange and it could also be related to PingPong Sky Modules. What am I doing wrong or not doing?

Related

How to mock a Validation Rule in Laravel

I want to mock a custom Validation rule (e.g. App\Rules\SomeRule). But when I run my test, it gives an Mockery\Exception\InvalidCountException: Method...should be called
exactly 1 times but called 0 times.
I've read Laravel's documentation on Mocking, on custom Validation Rules, Service Containers, Service Providers and I cannot figure out why I'm not successfully mocking the rule.
I read this thread but I'm struggling to connect it with my problem which is, "How can I test that my app is using this Rule". Or is that something I cannot test?
Here's my Rule
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class SomeRule implements Rule
{
public function passes($attribute, $value)
{
// some logic, returns TRUE if valid.
}
public function message()
{
//
}
}
My controller
namespace App\Http\Controllers;
use App\Rules\SomeRule;
use Illuminate\Http\Request;
class LoanController extends Controller
{
public function store(Request $request)
{
$request->validate([
'email' => ['required', new SomeRule]
]);
// ...insert in database, return json.
}
}
My Test
namespace Tests\Feature;
use Mockery;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Rules\SomeRule;
class LoansTest extends TestCase
{
use RefreshDatabase;
public function tearDown(): void
{
parent::tearDown();
Mockery::close();
}
/** #test */
public function the_sad_path__when_email_is_invalid()
{
$rule = Mockery::mock(SomeRule::class)->shouldReceive('passes')->once();
$this->app->instance(SomeRule::class, $rule);
$response = $this->json('POST', '/api/loans', ['email' => 'whatevs#gmail.com']);
}
}
I played with the idea of registering SomeRule in the AppServiceProvider. But that still didn't do anything:
public function register()
{
$this->app->bind(SomeRule::class, function ($app) {
return new SomeRule();
});
}
Code in Github
you need to use like this
class LoanController extends Controller
{
public function store(Request $request)
{
$request->validate([
'email' => ['required', new SomeRule()]
]);
// ...insert in database, return json.
}
}

Laravel: ORM independant Repository pattern

I stumbled upon different articles about implementing Repository pattern in Laravel and all of them making me confused. Everyone of them is putting their weight on "keeping it ORM independent" but no one is actually showing the example code.
I am here with my repository sample structure which I believe is not ORM independent in a way. But I need it with REAL REPOSITORY PATTERN solution where I can change the ORM from Eloquent model to something else like Doctrine. And keep the Business logic separate some way so i no need to change it. currently my Business logic lies in Repository (not recommended i think.)
Basic Questions:
My repository, uses Eloquent method name inside it which will not be there if i change the ORM. $this->model = $shops;
In Controllers and Blade templates what we are playing with is Collections of Eloquent Model. How should we handle them if we change the ORM?
Where to put create/delete methods if not in repository.
Please don't just use Domain Object word because I am tired of
understanding it without an coded example. It will be highly
appreciated if you try to explain [Domain object] using a real code
example by modifying this. [How to return it in controller and use it
in Blade]
Interface:
<?php
namespace Modules\ShopOwner\Entities\Repository\Contract;
interface ShopsRepository {
public function getall();
public function create($data);
public function update($id, $data);
public function delete($id);
}
Eloquent:
<?php
namespace Modules\ShopOwner\Entities\Repository\Eloquent;
use Illuminate\Database\Eloquent\Model;
use Modules\ShopOwner\Entities\Repository\Contract\ShopsRepository;
use Modules\ShopOwner\Entities\Shops;
class ShopsEloquent implements ShopsRepository
{
protected $model;
public function __construct(Shops $shops)
{
$this->model = $shops;
}
public function getall()
{
return $this->model::with('shopadmin')->get();
}
public function create($data)
{
$this->model::create($data);
}
public function update($id, $data)
{
$this->model::findOrFail($id)->update($data);
}
public function delete($id)
{
$this->model::findOrFail($id)->delete();
}
}
Controller:
<?php
namespace Modules\ShopOwner\Http\Controllers;
use App\Http\Controllers\Controller;
use Auth;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Modules\ShopOwner\Entities\Repository\Contract\ShopsRepository;
use Modules\ShopOwner\Entities\Shops;
use Validator;
class ShopsController extends Controller
{
protected $shops;
public function __construct(ShopsRepository $shops)
{
$this->shops = $shops;
}
public function index()
{
$shops = $this->shops->getall();
return view('shopowner::shops.list', compact('shops'));
}
public function store(Request $request)
{
$data = $request->all();
$this->shops->create($data);
return redirect()->route('SO.shops.index')->with('success', __('shopowner::shops.create_success'));
}
public function update(Request $request, $id)
{
$data = $request->all();
$this->shops->update($id, $data);
return redirect()->route('SO.shops.index')->with('success', __('shopowner::shops.update_success'));
}
public function delete($id)
{
$this->shops->delete($id);
return redirect()->route('SO.shops.index')->with('success', __('shopowner::shops.remove_success'));
}
}
Create a base repository first. then extend this repository. Try this way. It may help you
interface BaseRepository{
public function getall();
public function create($data);
public function update($id, $data);
public function delete($id);
}
class BaseEloquent implements BaseRepository{
protected $model;
public function getall(){
return $this->model->get();
}
....
....
}
interface ShopsRepository extends BaseRepository{
}
class ShopsEloquent extends BaseEloquent implements ShopsRepository{
public function __construct(Shops $shops){
$this->model = $shops;
}
public function getall(){
return $this->model::with('shopadmin')->get();
}
}

laravel create service provider

i make a serviceprovider and add provider in app.php but how can i use it ?
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Helpers\api\gg\gg;
class ApiServiceProvider extends ServiceProvider
{
protected $defer = true;
public function boot()
{
}
public function register()
{
$this->app->bind(gg::class, function ()
{
return new gg;
});
}
public function provides()
{
return [gg::class];
}
}
gg class is in App\Helpers\api\gg folder and i want use this class everywhere like that
gg::isReady();
app.php
'providers' => [
...
App\Providers\ApiServiceProvider::class,
...
]
homecontroller#index
public function index()
{
//how can use this provider in there ?
return view('pages.home');
}
When you did $this->app->bind(), you've bound an instance of a class to the IoC. When you bind to the IoC you make that available throughout the entirety of the application. HOWEVER:
Your namespaces break PSR-1 compliance. This is because you are not using StudlyCaps.
BAD: use App\Helpers\api\gg\gg
GOOD: use App\Helpers\Api\GG\GG.
Rename your folders/files accordingly. With that sorted, your bind function should actually change to a singleton. This is because you want a persistent state, not a reusable model.
$this->app->singleton(GG::class, function(){
return new GG;
});
You also should not check ->isReady() in every function, that's an example of an anti-pattern. Instead, this should be in a middleware:
php artisan make:middleware VerifyGGReady
Add this to your Kernel:
protected $routeMiddleware = [
//other definitions
'gg_ready' => App\Http\Middleware\VerifyGGReady::class
];
Update the handle() function in your middleware:
public function handle($request, Closure $next) {
if ($this->app->GG->isReady()) {
return $next($request);
}
return redirect('/'); //gg is not ready
});
And then either initialize it in your route groups:
Route::group(['middleware' => ['gg_ready']], function(){
//requires GG to be ready
});
Or directly on a route:
Route::get('acme', 'AcmeController#factory')->middleware('gg_ready');
Or use it in your controller:
$this->middleware('gg_ready');

Laravel 5 Resolving dependencies in ServiceProvider

I have a class which acts like a storage (add/get item). I try to bind it as a singleton in one service provider, and resolve it in another's boot method.
The code is changed for simplicity.
app/Providers/BindingProvider.php
<?php namespace App\Providers;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider as ServiceProvider;
class MyBindingFacade extends Facade {
public static function getFacadeAccessor() {
return 'my.binding';
}
}
class MyBinding {
protected $items = [];
public function add($name, $item) {
$this->items[$name] = $item;
}
public function get($name) {
return $this->items[$name];
}
public function getAll() {
return $this->items;
}
}
class BindingProvider extends ServiceProvider {
public function register() {
$this->app->singleton('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
}
public function provides() {
return [
'my.binding',
];
}
}
app/Providers/ResolvingProvider.php
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider as ServiceProvider;
use App\Providers\MyBinding;
class ResolvingProvider extends ServiceProvider {
public function boot(MyBinding $binding) {
$binding->add('foo', 'bar');
// $manual = $this->app->make('my.binding');
// $manual->add('foo', 'bar');
}
public function register() {}
}
app/Http/Controllers/WelcomeController.php
<?php
namespace App\Http\Controllers;
use App\Providers\MyBindingFacade;
class WelcomeController extends Controller {
public function index()
{
dd(MyBindingFacade::getAll()); // debug items
}
}
When I try to debug MyBinding state in my WelcomeController I'm getting empty item array. However, if I uncomment $manual part from my ResolvingProvider it returns an array containing 'foo' => 'bar'. Does it mean IoC resolution is broken in ServiceProvider::boot() method or am I misusing Laravel functionality?
Laravel version: 5.0.28
UPDATE: Added code sample from WelcomeController.
With this:
$this->app->singleton('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
You're saying: my.binding is a singleton and resolves to an instance of App\Providers\MyBinding.
That doesn't mean that App\Providers\MyBinding is registered as singleton too. What you should do instead is this:
$this->app->singleton('App\Providers\MyBinding');
$this->app->bind('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
Because the Facade binding uses $app->make() you should get the same instance you registered with $this->app->singleton() right above.
In the first example you are not using the Facade, you should be using:
use App\Providers\MyBindingFacade as MyBinding;
Which will in fact call make it using 'my.binding'.

Auth filter not working in Laravel

I'm building an API in Laravel to learn how to do such a thing. I'm following a Laracasts course to do this, but I can't get $this->beforeFilter('auth.basic'); to work. This is my controller:
class LessonsController extends ApiController {
protected $lessonstranformer;
function __construct(LessonTransformer $lessonTransformer)
{
$this->LessonTransformer = $lessonTransformer;
$this->beforeFilter('auth.basic');
}
public function index()
{
$lessons = Lessons::all();
return $this->respond([
'data' => $this->LessonTransformer->transformCollection($lessons->all())
]);
}
}
Probably one thing can cause the problem and that is:
$this->LessonTransformer = $lessonTransformer;
Should be:
$this->lessonTransformer = $lessonTransformer;
Because you have protected $lessonstranformer not protected $Lessonstranformer. Also make sure that, your ApiController extends the Controller or BaseController which may extended the Controller.

Categories