For my Laravel app I'm trying to create a Unit test that atttempts to get a count of all of the states I have in my State filter. The test does not need a user or roles or permissions to be able to run however with my test it extends this TestCase file. When I run my test its reporting that the roles table does not exist.
Does anyone have any ideas on what I should do?
<?php
namespace Tests;
use App\Models\User;
use App\Models\Role;
use App\Models\Permission;
use PHPUnit\Framework\Assert;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Foundation\Testing\TestResponse;
abstract class TestCase extends \Illuminate\Foundation\Testing\TestCase
{
use CreatesApplication;
protected $authorizedUser;
protected $unauthorizedUser;
/**
* The base URL to use while testing the application.
*
* #var string
*/
protected $baseUrl = 'http://ringside.app';
protected function setUp()
{
parent::setUp();
TestResponse::macro('data', function ($key) {
return $this->original->getData()[$key];
});
Collection::macro('assertContains', function ($value) {
Assert::assertTrue($this->contains($value), 'Failed asserting that the collection contains the specified value.');
});
Collection::macro('assertNotContains', function ($value) {
Assert::assertFalse($this->contains($value), 'Failed asserting that the collection does not contain the specified value.');
});
Collection::macro('assertEquals', function ($items) {
Assert::assertEquals(count($this), count($items));
$this->zip($items)->each(function ($pair) {
list($a, $b) = $pair;
Assert::assertTrue($a->is($b));
});
});
$this->setupUnauthorizedUser();
$this->setupWrestlerStatuses();
}
/**
* Creates an authorized user of the included permissions.
*
* #param array $slugs
* #return void
*/
protected function setupAuthorizedUser($slugs)
{
$this->authorizedUser = factory(User::class)->create();
$role = factory(Role::class)->create();
if (is_array($slugs)) {
foreach ($slugs as $slug) {
$permission = factory(Permission::class)->create(['slug' => $slug]);
$role->givePermissionTo($permission);
}
} else {
$permission = factory(Permission::class)->create(['slug' => $slugs]);
$role->givePermissionTo($permission);
}
$this->authorizedUser->assignRole($role);
}
protected function setupUnauthorizedUser()
{
$this->unauthorizedUser = factory(User::class)->create();
$role = factory(Role::class)->create();
$this->unauthorizedUser->assignRole($role);
}
}
<?php
namespace Tests\Unit\Utilities;
use Tests\TestCase;
class StateTest extends TestCase
{
/** #test */
public function it_can_get_all_states()
{
$this->assertEquals(50, count(State::all()));
}
}
Related
I have a StripeClient service provider which needs a key to instantiate:-
namespace App\Providers;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
use Stripe\StripeClient;
class StripeServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->singleton(StripeClient::class, function ($app) {
return new StripeClient(config('services.stripe.secret'));
});
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return [StripeClient::class];
}
Then a trait with a bunch of api call functions like this:-
trait StripeClientTrait
{
protected $stripe;
function __construct(StripeClient $stripeClient)
{
$this->stripe = $stripeClient;
}
/**
* #param User $user
*
* #return \Stripe\Customer
* #throws \Stripe\Exception\ApiErrorException
*/
function createCustomer(User $user)
{
return $this->stripe->customers->create([ 'name' => $user->fullname,
'email' => $user->email
]);
}
...
The trait works in a controller perfectly as expected:-
class SubscriptionContoller extends Controller
{
use StripeClientTrait;
public function checkout()
{
try {
$customer = $this->createCustomer(Auth::user());
if($checkoutSession = $this->createCheckoutSession($customer)) {
return redirect($checkoutSession->url);
}
} catch (ApiErrorException $ex){
Log::error($ex->getMessage());
return back()->with(['error'=>$ex->getMessage()]);
}
return back();
}
...
But I now need to use the trait in a model to provide access to some api functions.
class Company extends Tenant
{
use HasFactory, StripeClientTrait;
but adding the trait causes:-
Too few arguments to function App\Models\Company::__construct(), 0 passed in /home/vagrant/code/profiler/vendor/spatie/laravel-multitenancy/src/Models/Concerns/UsesTenantModel.php on line 13 and exactly 1 expected
Can anyone tell me how to implement the trait without using the constructor? I just need some static function helpers to lookup stuff on the API.
Thanks for any guidance :-)
having persevered I've found this way to use the service container in a model:-
public function getPrices()
{
$stripe = app(StripeClient::class);
return $stripe->prices->all(['active'=>true]);
}
But would still like to understand how to use the trait in the model, if anyone could explain I'd be grateful
Laravel CQRS
I am applying CQRS in Laravel just to learn how to use it.
I created a simple user registration and a controller that creates a command to dispatch the handle and use the right use case.
When Trying to use the interface in the controller, it looks like that I need to bind the interface and the implementation because it doesn't know which one to use but in this case I don't really understand how to bind the interface.
CreateUserController.php
<?php
declare(strict_types=1);
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Http\Requests\Users\CreateUserRequest;
use Illuminate\Http\RedirectResponse;
class CreateUserController extends Controller
{
public function __construct(private \Src\User\Infrastructure\CreateUserController $userController)
{
}
public function __invoke(CreateUserRequest $request): RedirectResponse
{
$this->userController->__invoke($request);
return redirect()->route('verify');
}
}
Src\User\Infrastructure\CreateUserController
<?php
declare(strict_types=1);
namespace Src\User\Infrastructure;
use App\Http\Requests\Users\CreateUserRequest;
use Src\Shared\Domain\Bus\Command\CommandBus;
use Src\User\Application\Create\CreateUserCommand;
final class CreateUserController
{
public function __construct(private CommandBus $commandBus)
{
}
public function __invoke(CreateUserRequest $request)
{
$name = $request->name;
$email = $request->email;
$password = $request->password;
$command = new CreateUserCommand($name, $email, $password);
$this->commandBus->dispatch($command);
}
}
CommandBus
<?php
declare(strict_types=1);
namespace Src\Shared\Domain\Bus\Command;
interface CommandBus
{
public function dispatch(Command $command): void;
}
Command
<?php
declare(strict_types=1);
namespace Src\Shared\Domain\Bus\Command;
interface Command
{
}
CreateUserCommandHandler
<?php
declare(strict_types=1);
namespace Src\User\Application\Create;
use Src\User\Domain\ValueObjects\UserEmail;
use Src\User\Domain\ValueObjects\UserName;
use Src\User\Domain\ValueObjects\UserPassword;
final class CreateUserCommandHandler
{
public function __construct(
private UserCreator $creator
)
{
}
public function __invoke(CreateUserCommand $command)
{
$name = new UserName($command->name());
$email = new UserEmail($command->email());
$password = new UserPassword($command->password());
$this->creator->__invoke($name, $email, $password);
}
}
The Error
I tried this:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Src\Shared\Domain\Bus\Command\Command;
use Src\User\Application\Create\CreateUserCommand;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(
Command::class,
CreateUserCommand::class
);
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
}
}
Here is how you can bind with the interface.
Create a class in app/Providers folder. You can give any name to this class. Eg. InterfaceServiceProvider. extends it with Illuminate\Support\ServiceProvider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class InterfaceServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->app->bind(YourInterFace::class, YourController::class);
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
//
}
}
Add this InterfaceServiceProvider in config/app.php in providers array
Eg.
'providers' => [
App\Providers\InterfaceServiceProvider::class,
]
In the example below (using PHP 7.3 ans PHPUnit 7.5.1), I have two mocks of the same class. I want to expect one of the two mocks as an argument in a method call. But the expectation seems to be satisfied if I call the method with the other mock.
<?php declare(strict_types=1);
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
public function testExample()
{
$mock1 = $this->getMockBuilder(stdClass::class)->getMock();
$mock2 = $this->getMockBuilder(stdClass::class)->getMock();
/** #var MockObject|ArrayObject $mock3 */
$mock3 = $this->getMockBuilder(ArrayObject::class)->setMethods(['append'])->getMock();
$this->assertEquals($mock1, $mock2); // OK
$this->assertNotSame($mock1, $mock2); // OK
$mock3->expects($this->once())->method('append')->with($mock1); // OK
// However I pass $mock2 instead of $mock1 !
$mock3->append($mock2);
// I would need something like :
// $mock3->expects($this->once())->method('append')->withSameAs($mock1); // Not OK
}
}
I think what you wanted is to call the $this->identicalTo function in the with function. That'll verify that the correct object has been passed. If you update the code to pass in the $mock1 it should fail because the objects aren't the same
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
public function testExample()
{
$mock1 = $this->getMockBuilder(stdClass::class)->getMock();
$mock2 = $this->getMockBuilder(stdClass::class)->getMock();
/** #var MockObject|ArrayObject $mock3 */
$mock3 = $this->getMockBuilder(ArrayObject::class)
->setMethods(['append'])->getMock();
$mock3->expects($this->once())->method('append')->with($this->identicalTo($mock2)); // OK
$mock3->append($mock2);
}
}
Other Example:
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
public function testExample()
{
$mock1 = $this->getMockBuilder(stdClass::class)->getMock();
$mock2 = $this->getMockBuilder(stdClass::class)->getMock();
/** #var MockObject|ArrayObject $mock3 */
$mock3 = $this->getMockBuilder(ArrayObject::class)
->setMethods(['append'])->getMock();
$class = new ExampleClass($mock3);
$mock3->expects($this->once())->method('append')->with($this->identicalTo($mock2)); // OK
$class->appendObject($mock2);
}
}
class ExampleClass
{
private $array;
public function __construct(ArrayObject $array)
{
$this->array = $array;
}
public function appendObject($stdClass)
{
$this->array->append($stdClass);
}
}
This is how I create helper (App\Helpers\Settings.php)
namespace App\Helpers;
use Illuminate\Database\Eloquent\Model;
class Settings {
protected $settings = [];
public function __construct() {
$this->settings['AppName'] = 'Test';
}
/**
* Fetch all values
*
* #return mixed
*/
public function getAll () {
return $this->settings;
}
}
Creating facade (App\Helpers\Facades\SettingsFacade.php)
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Settings extends Facade {
protected static function getFacadeAccessor() {
return 'Settings';
}
}
Creating Service Provider (App\Providers\SettingsServiceProvider.php)
namespace App\Providers;
use Illuminate\Support\Facades\App;
use Illuminate\Support\ServiceProvider;
class SettingsServiceProvider extends ServiceProvider {
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot() {
}
/**
* Register the service provider.
*
* #return void
*/
public function register() {
App::bind( 'Settings', function () {
return new \App\Helpers\Settings;
});
} */
}
Registering provider (App\Providers\SettingsServiceProvider::class)
Creating alias: 'Settings' => App\Facades\Settings::class
Running composer dump-autoload
Trying to use facade Settings::getAll();
Getting error Class 'App\Http\Controllers\Settings' not found
Can’t figure out why I cannot create facade and getting that error
try this one.
App\Helpers\Settings.php
namespace App\Helpers;
use Illuminate\Database\Eloquent\Model;
class Settings {
protected $settings = [];
public function __construct() {
$this->settings['AppName'] = 'Test';
}
/**
* Fetch all values
*
* #return mixed
*/
public function getAll () {
return $this->settings;
}
}
App/Http/Controllers/XyzController.php
use Facades\App\Settings;
class XyzController extends Controller
{
public function showView()
{
return Settings::getAll();
}
}
web.php
Route::get('/','XyzController#showView');
use Facades\App\Helpers\Settings;
Route::get('/direct',function() {
return Settings::getAll();
});
use laravel Real time facades
I am using Laravel 5.0 to create phpunit test alongside the actual model.
I get errors in phpunit tests but no errors when controller calls the model and it returned the desired data.
sample.php
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class sample extends Model {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'sample';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['id','username','details','image'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
public static function test()
{
return "Returned Text.";
}
public static function gettest()
{
return self::test();
}
public static function getItem()
{
return self::orderBy('username','asc')->get();
}
public static function findItem($id)
{
return self::find($id);
}
}
SampleTest.php
<?php namespace App;
use Mockery as m;
class SampleTest extends \PHPUnit_Framework_TestCase {
protected function setUp()
{
$this->mock = m::mock('App\sample')->makePartial();
}
protected function tearDown()
{
m::close();
}
/** #test */
public function should_return_string()
{
$response = $this->mock->test();
var_dump("test() returns :".$response);
}
/** #test */
public function should_return_string_from_test_function()
{
$response = $this->mock->gettest();
var_dump("gettest() returns :".$response);
}
/** #test */
public function should_return_mocked_data()
{
$this->mock->shouldReceive('test')->andReturn('Return Mocked Data');
$response = $this->mock->gettest();
var_dump("gettest() returns :".$response);
}
/** #test */
public function should_return_some_data_using_this_mock()
{
$this->mock->shouldReceive('get')->andReturn('hello');
$response = $this->mock->getItem();
}
}
Problem
When I use controller to call the model, it returned the desired data.
When I run phpunit on command prompt:-
test function is not mocked properly as it still returns the original string
getItem and findItem functions return an error saying
1) App\SampleTest::should_return_some_data_using_this_mock
BadMethodCallException: Static method Mockery_0_App_sample::getItem()
does not exist on this mock object
Question
How can I mock the function properly? Why it is saying the error code as shown above? Where was I doing it wrong?
Any help will be much appreciated.
Note: Test assertions is removed and replaced with var_dump to see the output on the command prompt.