Laravel Unit Test Controller - laravel

How can i test my function which has __construct ?
For ex my controller code looks like :
namespace App\Http\Controllers;
use App\Repositories\UserAccountRepository;
class UserController extends ProtectedController
{
protected $userAccountRepository;
public function __construct(userAccountRepository
$userAccountRepository){
parent::__construct();
$this->userAccountRepository = $userAccountRepository;
}
public function FunctionWantToTest(){
return 'string';
}
The unit test required to fulfill the construct first before test my FunctionWantToTest.
So, my test code looks like,
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use App\Http\Controllers\UserController;
class UserTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function testUserList()
{
$UserController = new UserController(???what value??);
$this->assertTrue(true);
}
}
Need more help (best practice) how to test the code which has construct.

I think you just need to use app->make:
class UserTest extends TestCase
{
protected $userController;
public function setUp() {
parent::setUp();
$this->userController = $this->app->make(UserController::class);
}
/**
* A basic test example.
*
* #return void
*/
public function testUserList()
{
$this->userController // do what you need here...
$this->assertTrue(true);
}
}

Use feature test or integration test when test controller,
You can see this brief explanation about unit test and integration test.
What's the difference between unit tests and integration tests?

Related

How to use laravel repository pattern searchable array?

I am using laravel-repository pattern ,i have one api which is responsible for getting all users it's working fine ,if we are using that package by default search should support for that i set $fieldSearchable array in the repository class.
i hit an api like this localhost.com/api/lists?search=foo,it's not working can you please help me where did i mistake
UserController.php
public function __construct(UserRepositoryInterface $repository)
{
$this->repository = $repository;
}
public function getUsers(){
$data = $this->repository->show();
return response()->json(fractal($data, new UserTransformer()));
}
UserRepositoryInterface.php
interface UserRepositoryInterface extends RepositoryInterface
{
public function show();
}
UserRepository.php
<?php
namespace App\Repositories;
use Prettus\Repository\Eloquent\BaseRepository;
use Prettus\Repository\Criteria\RequestCriteria;
use App\User as AppUser;
use App\UserSection;
use App\Validators\UserValidator;
use Illuminate\Support\Facades\DB;
/**
* Class UserRepositoryEloquent.
*
* #package namespace App\Repositories;
*/
class UserRepository extends BaseRepository implements UserRepositoryInterface
{
protected $fieldSearchable = ['phone_number'];
/**
* Specify Model class name
*
* #return string
*/
public function model()
{
return AppUser::class;
}
/**
* Boot up the repository, pushing criteria
*/
public function boot()
{
$this->pushCriteria(app(RequestCriteria::class));
}
public function show(){
return $this->model()::get();
}
}
It maybe resolved by utilising pre-difined methods No need to write show() function logic because by default l5-Repository pattern contains some methods to get all the data all()or paginate().in your controller write like this in getUsers()
$data = $this->repository->all();
or
$data = $this->repository->paginate('25');
all() is for fetch all the data from DB and paginate($limit) is fetch the data per page based on the limit.
if you are using any one of the above mentioned method then automatically search functionality will work

Laravel 8 - class not using mock version of class in test

Hi Latest question on unraveling code - I'm trying to create tests that mock certain classes but struggling to get my first attempt to work. It does not appear to be using the mock in the test but the real class.
There are three questions:
What do I need to change to get this to work and the mock to be used?
Does the mock work for the whole app under test or just the class under test?
If I wanted to return an array of values do I just create the array with the expected values to enable the class under test to use that returned data?
So here's the class I want to mock:
<?php
namespace App\Wedleague\Utility;
use App\Libraries\Utilities\FolderUtility;
use App\Wedleague\LeagueTable\LeagueTableInterface;
use PDF;
/**
* Class PDFGenerator
* Generates a PDF based on a league
* #package App\Libraries\Helpers
*/
class PDFGenerator
{
use FolderUtility;
/**
* set the folder location for the generated pdf
* #var string
*/
private string $storageFolder = 'pdf';
private string $path;
private LeagueTableInterface $leagueTable;
/**
* PDFGenerator constructor.
*/
public function __construct(LeagueTableInterface $leagueTable)
{
$this->path = storage_path('app/' . $this->storageFolder);
$this->setUp();
$this->leagueTable = $leagueTable;
}
/**
* #return \Barryvdh\DomPDF\PDF
*/
public function createLeaguePDF()
{
$this->leagueTable->getLeagueTable();
error_reporting(E_ALL ^ E_DEPRECATED);
return PDF::loadView($this->leagueTable->getViewPath(), $this->leagueTable->getViewData())
->setOptions(['fontSubsetting' => true])
->setPaper('a4', 'landscape')
->save(storage_path('app/pdf/' . $this->leagueTable->getLeagueType() . '_league_update.pdf'));
}
private function setUp()
{
$this->checkFolderExists($this->path);
}
}
Here's the class I'm trying to test that uses this class:
<?php
namespace App\Jobs;
use App\Wedleague\Utility\PDFGenerator;
use App\Mail\MailEclecticUpdate;
use App\Models\League;
use App\Wedleague\LeagueTable\EclecticLeagueTable;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
/**
* Class ProcessSendEclecticUpdate
* #package App\Jobs
*/
class ProcessSendEclecticUpdateEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of times the job may be attempted.
*
* #var int
*/
public $tries = 3;
/**
* The number of seconds the job can run before timing out.
*
* #var int
*/
public $timeout = 240;
private League $league;
public function __construct(League $league)
{
$this->league = $league;
}
public function handle()
{
if (!$this->league || $this->league->league_type !== 'eclectic') {
throw new Exception("Error Processing the job - league not found or invalid league", 1);
}
$PDFGenerator = new PDFGenerator(new EclecticLeagueTable($this->league));
$PDFGenerator->createLeaguePDF();
$this->league->player()->get()->each(function ($player) {
if ($player->contactEmail) {
Mail::to($player->contactEmail)
->queue(new MailEclecticUpdate(
$this->league
));
}
});
Log::info('Eclectic League update issued');
}
}
and here's the basics of the test:
<?php
namespace Tests\Jobs;
use App\Jobs\ProcessSendEclecticUpdateEmail;
use App\Mail\MailEclecticUpdate;
use App\Models\League;
use App\Models\User;
use App\Wedleague\Utility\PDFGenerator;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
/**
* Class ProcessSendEclecticUpdateTest
* #package Tests\Jobs
*/
class ProcessSendEclecticUpdateTest extends TestCase
{
use RefreshDatabase;
use WithFaker;
private $user;
private $adminUser;
protected function setUp(): void
{
parent::setUp();
$this->seed();
$this->adminUser = User::factory()->create(['admin' => 1]);
}
/**
* #test
* #covers ProcessSendEclecticUpdateEmail::handle
* #description:
*/
public function testHandle()
{
$mock = $this->mock(\App\Wedleague\Utility\PDFGenerator::class);
$mock->shouldReceive('createLeaguePDF')->once();
$this->app->instance(PDFGenerator::class, $mock);
Mail::fake();
$this->withoutExceptionHandling();
$league = League::find(27);
$players = $league->player()
->get()
->filter(function ($player){
return $player->contactEmail;
});
ProcessSendEclecticUpdateEmail::dispatch($league);
Mail::assertQueued(MailEclecticUpdate::class, $players->count());
}
}
The response I get from the test is:
Mockery\Exception\InvalidCountException : Method createLeaguePDF(<Any Arguments>) from Mockery_2_App_Wedleague_Utility_PDFGenerator should be called
exactly 1 times but called 0 times.
I know this is not using the mock due to the length of time the test takes as it produces a PDF
Any ideas how I can get this to work - it's mocking me!!! :)

How to automatically create sqlite test db when executing parallel tests?

When executing tests separately I have added this functions to make sure sqlite db would be created if it's not there:
abstract class TestCase extends BaseTestCase
{
use CreatesApplication, DatabaseMigrations;
public function setUp(): void
{
$this->initializeTestDB();
parent::setUp();
Notification::fake();
if ($this->tenancy) {
$this->initializeTenancy();
}
}
public function initializeTestDB(): void
{
$dbname = env('DB_DATABASE');
$filePath= "./" . $dbname;
if (!file_exists($filePath)) {
touch($filePath);
}
}
But this does not help when using php artisan test --parallel. I have tried to move this method into AppServiceProvider as in the documentation. Also didn't helped:
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
ParallelTesting::setUpProcess(function ($token) {
$this->handleTestDB();
});
Any ideas to handle this properly for both parallel and separate tests ?

How to mock classes within command classes in Laravel

I am using Laravel 5.3. For a procedur I am using a command. The command class is calling a method of an another class. I wanted to unit test it with phpunit. Therefore I mocked up the class within the command class. When I run the test, then the actual method of class is running and not the mocked method. Bevor I have implemented the functinality of the command in a job. There I can call the mocked method without any problem.
The test class looks like this:
class CommandTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->api = $this->getMockBuilder('App\Services\APIRepository')
->setMethods(['getStatus'])
->getMock();
}
/** #test */
public function test_api()
{
...
$this->api->expects($this->any())
->method('getStatus')
->will($this->returnValue($api_response));
\Artisan::call('myapp:tracker',[]);
...
}
}
class Command extends Command
{
protected $signature = 'myapp:tracker';
private $api;
public function __construct(APIRepository $api)
{
$this->api = $api;
}
public function handle()
{
...
$status = $this->api->getStatus(...);
var_dump($status);
...
}
}
The output of var_dump($status) is: NULL
Do I need a special method for mocking a class within a command class?
Thanks a lot in advanced!

Laravel: use extended controller or Traits or something else?

To maintain my Laravel application and save myself from a lot of duplicate code I have made the following solution:
BaseController
class BaseController extends Controller
{
public function get($id){
return $this->baseService->get($id);
}
public function getAll(){
return $this->baseService->getAll();
}
}
BaseService
class BaseService
{
protected $model;
public function __construct($model){
$this->model = $model;
}
public function get($id){
return response()->json($this->model->where('id', $id)->first());
}
public function getAll()
{
return $this->model->get();
}
}
MyController
class MyController extends BaseController
{
protected $model;
protected $baseService;
public function __construct(){
$this->model= new Model();
$this->baseService = new BaseService($this->model);
}
/**
* This controller has all the functionality from BaseController now
*/
}
What I'm wondering if this is a good method. Should I stick with this or should I use a different approach? I've heard about Traits but not sure if they are doing the same thing. It's Laravel 5.5 I'm using.
Yes, traits are used to move methods out of a controller regularly. A good example that the Laravel framework uses is the ThrottlesLogin trait. Take a look at https://github.com/laravel/framework/blob/5.5/src/Illuminate/Foundation/Auth/ThrottlesLogins.php#L20
to see how the methods are moved outside of a controller but can be still accessed by importing the trait using the use keyword.
While traits would work for your use case I wouldn't use them here for the functionality you are looking for. I would use the repository pattern. It would better separate your code and make it more reusable.
Take a look at https://bosnadev.com/2015/03/07/using-repository-pattern-in-laravel-5/ for more information on the repository pattern. Basically, you would separate your code into a separate repository and use Laravel's built in IoC to inject the repository into your controller.
MyController
class MyController extends Controller
{
protected $repo;
public function __construct(MyRepository $myRepository)
{
$this->repo = $myRepository;
}
public function index()
{
$myStuff = $this->repo->all();
}
// you can also inject the repository directly in the controller
// actions.
// look at https://laravel.com/docs/5.5/controllers#dependency-injection-and-controllers
public function other(MyRepository $repo)
{
$myStuff = $repo->all();
}
}
This is the perfect use case for a Trait. Traits are intended for reusable functions. They're super simple to implement, and won't take more than a few minutes to change what you have.
Here is a great article on them: https://www.conetix.com.au/blog/simple-guide-using-traits-laravel-5

Resources