feat: complete Smartgift AI Laravel unit testing toolkit
- Add 10 core testing guides (setup, AAA principles, PHPUnit/Pest, mocking, anti-patterns, PHP 8.4+, debugging, data factories, advanced edge cases) - Add 13 component testing guides covering all Laravel layers - Add standard templates for PHPUnit 10/11 and Pest v2/v3 including ArchitectureTest - Add Docker smart test runner, dependency installer, and CI/CD templates (GitHub Actions, GitLab CI) - Add 4 Claude Code skills (laravel-test-setup, laravel-unit-test with --all mode, laravel-test-review, laravel-mock-helper) - Add single unified installer script (sg-install-unit-testing.sh) with global support, CLI runners, and 1-click updater (sg-test-update) - Add comprehensive documentation (README.md, USAGE-GUIDE.md) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use {{TargetClassFullName}};
|
||||
|
||||
beforeEach(function () {
|
||||
// $this->action = new {{ClassName}}(...);
|
||||
});
|
||||
|
||||
describe('{{ClassName}}', function () {
|
||||
it('handles action invocation successfully', function () {
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
// $result = ($this->action)($dto);
|
||||
|
||||
// Assert
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pest PHP Architecture Test Suite (Enterprise Pro Level)
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Bộ kiểm thử kiến trúc toàn diện bảo vệ cấu trúc mã nguồn không bị suy thoái.
|
||||
| Chạy bằng lệnh: ./test-runner.sh tests/Unit/ArchitectureTest.php
|
||||
|
|
||||
*/
|
||||
|
||||
describe('Architecture & Code Quality Gates', function () {
|
||||
|
||||
test('All files must use strict types')
|
||||
->expect('App')
|
||||
->toUseStrictTypes();
|
||||
|
||||
test('No debugging functions left behind in codebase')
|
||||
->expect(['dd', 'dump', 'ray', 'var_dump', 'print_r', 'die', 'exit'])
|
||||
->not->toBeUsed();
|
||||
|
||||
test('Controllers naming convention & inheritance')
|
||||
->expect('App\Http\Controllers')
|
||||
->toHaveSuffix('Controller');
|
||||
|
||||
test('Controllers should not directly query Eloquent Models')
|
||||
->expect('App\Http\Controllers')
|
||||
->not->toUse('App\Models')
|
||||
->ignoring('App\Http\Controllers\Auth');
|
||||
|
||||
test('Services must be final or readonly')
|
||||
->expect('App\Services')
|
||||
->toHaveSuffix('Service');
|
||||
|
||||
test('DTOs and Value Objects must be immutable (readonly)')
|
||||
->expect('App\DTOs')
|
||||
->toBeReadonly();
|
||||
|
||||
test('Custom Validation Rules must implement ValidationRule contract')
|
||||
->expect('App\Rules')
|
||||
->toImplement('Illuminate\Contracts\Validation\ValidationRule');
|
||||
|
||||
test('Jobs must be queueable')
|
||||
->expect('App\Jobs')
|
||||
->toImplement('Illuminate\Contracts\Queue\ShouldQueue');
|
||||
|
||||
test('Events must not have dependencies injected')
|
||||
->expect('App\Events')
|
||||
->classes()
|
||||
->not->toHaveConstructor();
|
||||
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
describe('{{CommandSignature}} Command', function () {
|
||||
it('executes artisan command successfully', function () {
|
||||
$this->artisan('{{CommandSignature}}')
|
||||
->assertExitCode(Command::SUCCESS);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use {{TargetClassFullName}};
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
beforeEach(function () {
|
||||
// $this->controller = new {{ClassName}}(...);
|
||||
});
|
||||
|
||||
describe('{{ClassName}}', function () {
|
||||
it('returns successful response', function () {
|
||||
// Arrange
|
||||
// $request = Request::create('/api/resource', 'GET');
|
||||
|
||||
// Act
|
||||
// $response = $this->controller->index($request);
|
||||
|
||||
// Assert
|
||||
// expect($response->getStatusCode())->toBe(Response::HTTP_OK);
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use {{TargetClassFullName}};
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
describe('{{ClassName}}', function () {
|
||||
it('authorizes request appropriately', function () {
|
||||
$request = new {{ClassName}}();
|
||||
expect($request->authorize())->toBeTrue();
|
||||
});
|
||||
|
||||
it('passes validation with valid payload', function () {
|
||||
$request = new {{ClassName}}();
|
||||
$validator = Validator::make([
|
||||
// Valid data here
|
||||
], $request->rules());
|
||||
|
||||
expect($validator->fails())->toBeFalse();
|
||||
});
|
||||
|
||||
it('fails validation when required fields are missing', function () {
|
||||
$request = new {{ClassName}}();
|
||||
$validator = Validator::make([], $request->rules());
|
||||
|
||||
expect($validator->fails())->toBeTrue();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use {{TargetClassFullName}};
|
||||
|
||||
describe('{{ClassName}}', function () {
|
||||
it('handles job execution successfully', function () {
|
||||
// Arrange
|
||||
// $dependencyMock = Mockery::mock(Service::class);
|
||||
// $job = new {{ClassName}}(...);
|
||||
|
||||
// Act
|
||||
// $job->handle($dependencyMock);
|
||||
|
||||
// Assert
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use {{TargetClassFullName}};
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->middleware = new {{ClassName}}();
|
||||
});
|
||||
|
||||
describe('{{ClassName}}', function () {
|
||||
it('passes request to next closure when authorized', function () {
|
||||
$request = Request::create('/test-route', 'GET');
|
||||
$nextCalled = false;
|
||||
|
||||
$next = function ($req) use (&$nextCalled) {
|
||||
$nextCalled = true;
|
||||
return response('OK', Response::HTTP_OK);
|
||||
};
|
||||
|
||||
$response = $this->middleware->handle($request, $next);
|
||||
|
||||
expect($nextCalled)->toBeTrue();
|
||||
expect($response->getStatusCode())->toBe(Response::HTTP_OK);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use {{TargetClassFullName}};
|
||||
use App\Models\User;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->policy = new {{ClassName}}();
|
||||
});
|
||||
|
||||
describe('{{ClassName}}', function () {
|
||||
it('allows authorized user to perform action', function () {
|
||||
$user = new User(['id' => 1]);
|
||||
// expect($this->policy->view($user, $model))->toBeTrue();
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
it('denies unauthorized user from performing action', function () {
|
||||
$user = new User(['id' => 99]);
|
||||
// expect($this->policy->update($user, $model))->toBeFalse();
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use {{TargetClassFullName}};
|
||||
|
||||
beforeEach(function () {
|
||||
// $this->dependencyMock = Mockery::mock(DependencyInterface::class);
|
||||
// $this->service = new {{ClassName}}($this->dependencyMock);
|
||||
});
|
||||
|
||||
describe('{{ClassName}}', function () {
|
||||
it('executes successfully under normal conditions', function () {
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
|
||||
// Assert
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
it('throws exception when input is invalid', function () {
|
||||
// expect(fn () => $this->service->method(...))->toThrow(\InvalidArgumentException::class);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user