feat: ✨ add unit tests laravel package

This commit is contained in:
Alexis Gousseau 2024-06-14 14:27:50 +02:00
parent 2d904b5593
commit 42fa65ed0a
15 changed files with 1157 additions and 441 deletions

1
.gitignore vendored
View file

@ -23,6 +23,7 @@ Homestead.yaml
npm-debug.log
yarn-error.log
.npmrc
/tests/coverage
# Ignore public favicon
/public/android-chrome-192x192.png

View file

@ -20,9 +20,11 @@
"laravel/ui": "^4.2",
"mews/purifier": "^3.4",
"sentry/sentry-laravel": "*",
"uwil/k8s-laravel": "^1.2"
"uwil/k8s-laravel": "^1.2",
"uwil/unit-tests-laravel": "^1.0"
},
"require-dev": {
"brianium/paratest": "^7.4",
"fakerphp/faker": "^1.9.1",
"kitloong/laravel-migrations-generator": "^6.8",
"krlove/eloquent-model-generator": "dev-l10-compatibility",
@ -52,6 +54,13 @@
{
"type": "composer",
"url": "https://lab.s-web.fr/api/v4/group/23/-/packages/composer/packages.json"
},
{
"type": "path",
"url": "packages/unit-tests-laravel",
"options": {
"symlink": true
}
}
],
"autoload-dev": {

943
composer.lock generated

File diff suppressed because it is too large Load diff

61
config/unit-tests.php Normal file
View file

@ -0,0 +1,61 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| View options
|--------------------------------------------------------------------------
|
| Here you can configure the optional prefix of existing views and all their
| file names.
|
*/
'view' => [
'prefix' => 'bo.',
'name-homepage' => 'dashboard',
'resources-index' => 'index',
'resources-create' => 'create',
'resources-update' => 'edit',
'resources-read' => 'show',
],
/*
|--------------------------------------------------------------------------
| Route options
|--------------------------------------------------------------------------
|
| Here you can configure the optional prefix of existing routes and all
| their action names.
|
*/
'route' => [
'prefix' => 'bo.',
'action-create' => 'store',
'action-update' => 'update',
'action-delete' => 'destroy',
],
/*
|--------------------------------------------------------------------------
| Model field used for an update
|--------------------------------------------------------------------------
|
| Here you can configure the list of possible text fields existing in a model,
| (the first one to exist will be updated, must be a text field).
| ex: ['name', 'label', 'title', 'first_name'].
|
*/
'list-fields' => [
'name',
'label',
'title',
'first_name',
]
];

View file

@ -0,0 +1,32 @@
<?php
namespace Database\Factories;
use App\Enums\ActivityLogs\ActivityLogsEventEnum;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<\App\Models\ActivityLog>
*/
final class ActivityLogFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$boolean = fake()->boolean(50);
return [
'user_id' => null,
'is_anonymous' => $boolean,
'is_console' => !$boolean,
'model_class' => "\App\Models\User",
'model_id' => 1,
'event' => \collect(ActivityLogsEventEnum::toArray())->random()->value,
'data' => [],
'created_at' => now(),
];
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Database\Factories;
use App\Enums\StaticPageType;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends Factory<\App\Models\StaticPage>
*/
final class StaticPageFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$title = fake()->words(rand(1, 2), true);
return [
'seo_title' => fake()->sentence(),
'seo_description' => fake()->sentence(),
'title' => fake()->words(rand(1, 2), true),
'slug' => Str::slug($title),
'type' => \collect(StaticPageType::toArray())->random()->value,
'content' => fake()->sentence(),
];
}
}

View file

@ -1,31 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory suffix=".php">./app</directory>
</include>
</coverage>
<php>
<env name="APP_ENV" value="testing"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_DRIVER" value="array"/>
<!-- <env name="DB_CONNECTION" value="sqlite"/> -->
<!-- <env name="DB_DATABASE" value=":memory:"/> -->
<env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="TELESCOPE_ENABLED" value="false"/>
</php>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.3/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true">
<coverage includeUncoveredFiles="true">
<report>
<html outputDirectory="tests/coverage" />
</report>
</coverage>
<testsuites>
<testsuite name="Tests">
<directory suffix="Test.php">
./tests/Back
</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing" />
<env name="BCRYPT_ROUNDS" value="4" />
<env name="CACHE_DRIVER" value="array" />
<env name="MAIL_MAILER" value="array" />
<env name="QUEUE_DRIVER" value="database" />
<env name="QUEUE_CONNECTION" value="database" />
<env name="SESSION_DRIVER" value="array" />
<env name="TELESCOPE_ENABLED" value="false" />
</php>
<source>
<include>
<directory suffix=".php">
./app
</directory>
</include>
</source>
</phpunit>

View file

@ -0,0 +1,76 @@
<?php
namespace Tests\Back;
use App\Enums\Users\RoleEnum;
use App\Models\ActivityLog;
use App\Models\User as AuthModel;
use Tests\TestCase;
class ActivityLogTest extends TestCase
{
/**
* TESTS GUEST CANNOT ACCESS VIEWS.
*/
/** @return void */
public function testGuestCannotAccessActivityLogsIndexView(): void
{
$response = $this->get(route(
config('unit-tests.route.prefix') .
'activity_logs.' .
config('unit-tests.view.resources-index')
));
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/** @return void */
public function testGuestCannotAccessActivityLogsReadView(): void
{
$model = ActivityLog::factory()->createOneQuietly();
$response = $this->get(route(
config('unit-tests.route.prefix') .
'activity_logs.' .
config('unit-tests.view.resources-read'),
$model->getKey()
));
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/**
* TESTS USER CONCEPTOR ACCESS VIEWS.
*/
/** @return void */
public function testUserConceptorCanAccessActivityLogsIndexView(): void
{
$authModel = AuthModel::factory()->createOneQuietly();
$authModel->update(['role' => RoleEnum::conceptor]);
$response = $this->actingAs($authModel, 'backend')->get(
route(config('unit-tests.route.prefix') . 'activity_logs.' . config('unit-tests.view.resources-index'))
);
$response->assertSuccessful();
$response->assertViewIs(
config('unit-tests.view.prefix') .
'pages.activity_logs.' .
config('unit-tests.view.resources-index')
);
}
/** @return void */
public function testUserConceptorCanAccessActivityLogsReadView(): void
{
$authModel = AuthModel::factory()->createOneQuietly();
$authModel->update(['role' => RoleEnum::conceptor]);
$model = ActivityLog::factory()->createOneQuietly();
$response = $this->actingAs($authModel, 'backend')->get(
route(config('unit-tests.route.prefix') . 'activity_logs.' . config('unit-tests.view.resources-read'), $model)
);
$response->assertSuccessful();
$response->assertViewIs(
config('unit-tests.view.prefix') .
'pages.activity_logs.' .
config('unit-tests.view.resources-read')
);
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace Tests\Back;
use App\Models\User as AuthModel;
use Tests\TestCase;
class AuthenticationTest extends TestCase
{
/** @return void */
public function testGuestCanViewLoginForm(): void
{
$response = $this->get(route(config('unit-tests.route.prefix') . 'login'));
$response->assertSuccessful();
$response->assertViewIs(config('unit-tests.view.prefix') . 'auth.login');
}
/** @return void */
public function testGuestCannotAccessAdminDashboard(): void
{
$response = $this->get(route(config('unit-tests.route.prefix') . config('unit-tests.view.name-homepage')));
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/** @return void */
public function testUserCanAccessAdminDashboard(): void
{
// @phpstan-ignore-next-line
$authModel = AuthModel::factory()->create();
$response = $this->actingAs($authModel, 'backend')
->get(route(config('unit-tests.route.prefix') . config('unit-tests.view.name-homepage')));
$response->assertSuccessful();
$response->assertViewIs(config('unit-tests.view.prefix') . 'pages.' . config('unit-tests.view.name-homepage'));
}
/** @return void */
public function testUserCannotViewLoginFormWhenAuthenticated(): void
{
// @phpstan-ignore-next-line
$authModel = AuthModel::factory()->make();
$response = $this->actingAs($authModel, 'backend')->get(route(config('unit-tests.route.prefix') . 'login'));
$response->assertStatus(302);
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace Tests\Back;
use App\Enums\Users\RoleEnum;
use App\Models\StaticPage;
use App\Models\User as AuthModel;
use Tests\TestCase;
class StaticPageTest extends TestCase
{
/**
* TESTS GUEST CANNOT ACCESS VIEWS.
*/
/** @return void */
public function testGuestCannotAccessStaticPagesIndexView(): void
{
$response = $this->get(route(
config('unit-tests.route.prefix') .
'static_pages.' .
config('unit-tests.view.resources-index')
));
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/** @return void */
public function testGuestCannotAccessStaticPagesReadView(): void
{
$model = StaticPage::factory()->createOneQuietly();
$response = $this->get(route(
config('unit-tests.route.prefix') .
'static_pages.' .
config('unit-tests.view.resources-read'),
$model->getKey()
));
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/**
* TESTS USER CONCEPTOR ACCESS VIEWS.
*/
/** @return void */
public function testUserConceptorCanAccessStaticPagesIndexView(): void
{
$authModel = AuthModel::factory()->createOneQuietly();
$authModel->update(['role' => RoleEnum::conceptor]);
$response = $this->actingAs($authModel, 'backend')->get(
route(config('unit-tests.route.prefix') . 'static_pages.' . config('unit-tests.view.resources-index'))
);
$response->assertSuccessful();
$response->assertViewIs(
config('unit-tests.view.prefix') .
'pages.static_pages.' .
config('unit-tests.view.resources-index')
);
}
/** @return void */
public function testUserConceptorCanAccessStaticPagesReadView(): void
{
$authModel = AuthModel::factory()->createOneQuietly();
$authModel->update(['role' => RoleEnum::conceptor]);
$model = StaticPage::factory()->createOneQuietly();
$response = $this->actingAs($authModel, 'backend')->get(
route(config('unit-tests.route.prefix') . 'static_pages.' . config('unit-tests.view.resources-read'), $model)
);
$response->assertSuccessful();
$response->assertViewIs(
config('unit-tests.view.prefix') .
'pages.static_pages.' .
config('unit-tests.view.resources-read')
);
}
}

218
tests/Back/UserTest.php Normal file
View file

@ -0,0 +1,218 @@
<?php
namespace Tests\Back;
use App\Enums\Users\RoleEnum;
use App\Models\User as AuthModel;
use App\Models\User;
use Illuminate\Support\Facades\Schema;
use Tests\TestCase;
class UserTest extends TestCase
{
/**
* TESTS GUEST CANNOT ACCESS VIEWS.
*/
/** @return void */
public function testGuestCannotAccessUsersIndexView(): void
{
$response = $this->get(route(
config('unit-tests.route.prefix') .
'users.' .
config('unit-tests.view.resources-index')
));
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/** @return void */
public function testGuestCannotAccessUsersReadView(): void
{
$model = User::factory()->createOneQuietly();
$response = $this->get(route(
config('unit-tests.route.prefix') .
'users.' .
config('unit-tests.view.resources-read'),
$model->getKey()
));
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/** @return void */
public function testGuestCannotAccessUsersCreateView(): void
{
$response = $this->get(route(
config('unit-tests.route.prefix') .
'users.' .
config('unit-tests.view.resources-create')
));
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/** @return void */
public function testGuestCannotAccessUsersUpdateView(): void
{
$model = User::factory()->createOneQuietly();
$response = $this->get(route(
config('unit-tests.route.prefix') .
'users.' .
config('unit-tests.view.resources-update'),
$model->getKey()
));
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/**
* TESTS GUEST CANNOT POST MODEL.
*/
/** @return void */
public function testGuestCannotCreateUser(): void
{
$model = User::factory()->createOneQuietly();
$response = $this->post(
route(
config('unit-tests.route.prefix') . 'users.' . config('unit-tests.route.action-create'),
$model->getKey()
),
$model->toArray()
);
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/** @return void */
public function testGuestCannotUpdateUser(): void
{
$model = User::factory()->createOneQuietly();
$response = $this->patch(
route(
config('unit-tests.route.prefix') . 'users.' . config('unit-tests.route.action-update'),
$model->getKey()
),
$model->toArray()
);
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/** @return void */
public function testGuestCannotDeleteUser(): void
{
$model = User::factory()->createOneQuietly();
$response = $this->delete(
route(
config('unit-tests.route.prefix') . 'users.' . config('unit-tests.route.action-delete'),
$model->getKey()
),
$model->toArray()
);
$response->assertRedirect(route(config('unit-tests.route.prefix') . 'login'));
}
/**
* TESTS USER CONCEPTOR ACCESS VIEWS.
*/
/** @return void */
public function testUserConceptorCanAccessUsersIndexView(): void
{
$authModel = AuthModel::factory()->createOneQuietly();
$authModel->update(['role' => RoleEnum::conceptor]);
$response = $this->actingAs($authModel, 'backend')->get(
route(config('unit-tests.route.prefix') . 'users.' . config('unit-tests.view.resources-index'))
);
$response->assertSuccessful();
$response->assertViewIs(
config('unit-tests.view.prefix') .
'pages.users.' .
config('unit-tests.view.resources-index')
);
}
/** @return void */
public function testUserConceptorCanAccessUsersReadView(): void
{
$authModel = AuthModel::factory()->createOneQuietly();
$authModel->update(['role' => RoleEnum::conceptor]);
$model = User::factory()->createOneQuietly();
$response = $this->actingAs($authModel, 'backend')->get(
route(config('unit-tests.route.prefix') . 'users.' . config('unit-tests.view.resources-read'), $model)
);
$response->assertSuccessful();
$response->assertViewIs(
config('unit-tests.view.prefix') .
'pages.users.' .
config('unit-tests.view.resources-read')
);
}
/** @return void */
public function testUserConceptorCanAccessUsersCreateView(): void
{
$authModel = AuthModel::factory()->createOneQuietly();
$authModel->update(['role' => RoleEnum::conceptor]);
$response = $this->actingAs($authModel, 'backend')->get(
route(config('unit-tests.route.prefix') . 'users.' . config('unit-tests.view.resources-create'))
);
$response->assertSuccessful();
$response->assertViewIs(
config('unit-tests.view.prefix') .
'pages.users.' .
config('unit-tests.view.resources-create')
);
}
/** @return void */
public function testUserConceptorCanAccessUsersUpdateView(): void
{
$authModel = AuthModel::factory()->createOneQuietly();
$authModel->update(['role' => RoleEnum::conceptor]);
$model = User::factory()->createOneQuietly();
$response = $this->actingAs($authModel, 'backend')->get(
route(config('unit-tests.route.prefix') . 'users.' . config('unit-tests.view.resources-update'), $model)
);
$response->assertSuccessful();
$response->assertViewIs(
config('unit-tests.view.prefix') .
'pages.users.' .
config('unit-tests.view.resources-update')
);
}
/**
* TESTS CAN ACTIONS ON MODEL.
*/
/** @return void */
public function testCanCreateUser(): void
{
$model = User::factory()->createOneQuietly();
$this->assertModelExists($model);
}
/** @return void */
public function testCanUpdateUser(): void
{
$model = User::factory()->createOneQuietly();
$fieldTest = "";
foreach (config('unit-tests.list-fields') as $field) {
if (Schema::hasColumn($model->getTable(), $field)) {
$model->update([$field => "test"]);
$fieldTest = $field;
break;
}
}
$this->assertTrue($model->wasChanged());
$this->assertTrue(array_key_exists($fieldTest, $model->getChanges()));
$this->assertModelExists($model);
}
/** @return void */
public function testCanDeleteUser(): void
{
$model = User::factory()->createOneQuietly();
$model->delete();
$this->assertModelMissing($model);
}
}

View file

@ -3,19 +3,18 @@
namespace Tests;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Application;
trait CreatesApplication
{
/**
* Creates the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication(): Application
public function createApplication(): \Illuminate\Foundation\Application
{
$app = require __DIR__.'/../bootstrap/app.php';
$app = require __DIR__ . '/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
return $app;
}
}

View file

@ -1,19 +0,0 @@
<?php
namespace Tests\Feature;
// use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*/
public function test_the_application_returns_a_successful_response(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}

View file

@ -2,9 +2,11 @@
namespace Tests;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
use DatabaseTransactions;
}

View file

@ -1,16 +0,0 @@
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*/
public function test_that_true_is_true(): void
{
$this->assertTrue(true);
}
}