Veldora includes an expressive testing framework built on top of PHPUnit. Write feature tests that simulate real HTTP requests, assert on responses, and verify database state — without needing a real browser.
Setting Up Tests
Tests live in the tests/ directory. Run the full test suite with:
Terminal • bash
php vendor/bin/phpunitOr run a single test file:
Terminal • bash
php vendor/bin/phpunit tests/Feature/PostTest.phpWriting HTTP Feature Tests
app/Models/Post.php
PHP
namespace Tests\Feature;
use Veldora\Framework\Testing\TestCase;
use Database\Factories\UserFactory;
use Database\Factories\PostFactory;
class PostTest extends TestCase
{
public function test_guests_cannot_create_posts(): void
{
$response = $this->post('/posts', ['title' => 'Test', 'body' => 'Body']);
$response->assertStatus(302);
$response->assertRedirect('/login');
}
public function test_authenticated_user_can_create_post(): void
{
$user = (new UserFactory())->create();
$response = $this->actingAs($user)->post('/posts', [
'title' => 'My First Post',
'body' => 'This is the body content.',
]);
$response->assertRedirect('/posts');
$this->assertDatabaseHas('posts', ['title' => 'My First Post']);
}
public function test_user_can_view_their_posts(): void
{
$user = (new UserFactory())->create();
$post = (new PostFactory())->create(['user_id' => $user->id, 'title' => 'My Post']);
$response = $this->actingAs($user)->get('/posts');
$response->assertOk();
$response->assertSee('My Post');
}
public function test_user_cannot_delete_another_users_post(): void
{
$owner = (new UserFactory())->create();
$other = (new UserFactory())->create();
$post = (new PostFactory())->create(['user_id' => $owner->id]);
$response = $this->actingAs($other)->delete('/posts/' . $post->id);
$response->assertStatus(403);
$this->assertDatabaseHas('posts', ['id' => $post->id]); // Still exists
}
}All Response Assertion Methods
example.php
PHP
// HTTP status
$response->assertOk(); // status 200
$response->assertStatus(201); // specific status code
$response->assertRedirect('/login'); // 302 redirect to URL
$response->assertNotFound(); // 404
$response->assertForbidden(); // 403
// Response body
$response->assertSee('Welcome'); // Contains string
$response->assertDontSee('Error'); // Does NOT contain string
$response->assertSeeText('Dashboard'); // Contains plain text
// JSON responses
$response->assertJson(['status' => 'ok']); // JSON has these keys/values
$response->assertJsonCount(5, 'data'); // JSON array has N items
$response->assertJsonPath('data.0.title', 'Post 1'); // Specific JSON path valueDatabase Assertions
example.php
PHP
// Assert a record exists in the database
$this->assertDatabaseHas('posts', [
'title' => 'My Post',
'user_id' => 42,
]);
// Assert a record does NOT exist in the database
$this->assertDatabaseMissing('posts', ['title' => 'Deleted Post']);
// Assert a record was soft-deleted (deleted_at is not null)
$this->assertSoftDeleted('posts', ['id' => 1]);Model Factories
Factories let you generate fake model instances for tests and seeders without writing manual SQL or repetitive User::create(...) calls.
Generate a factory:
Terminal • bash
php veldora make:factory PostFactoryDefine the factory:
app/Models/Post.php
PHP
// database/factories/PostFactory.php
namespace Database\Factories;
use App\Models\Post;
use Veldora\Framework\Database\Factory;
class PostFactory extends Factory
{
protected string $model = Post::class;
public function definition(): array
{
return [
'user_id' => (new UserFactory())->create()->id,
'title' => $this->faker->sentence(6),
'slug' => $this->faker->slug(),
'body' => $this->faker->paragraphs(3, true),
'is_published' => 1,
'published_at' => $this->faker->dateTimeBetween('-1 year', 'now')->format('Y-m-d H:i:s'),
];
}
}Use factories in tests:
example.php
PHP
// Create a single instance and persist to database
$post = (new PostFactory())->create();
// Create with overridden attributes
$draftPost = (new PostFactory())->create(['is_published' => 0]);
// Create multiple instances
$posts = (new PostFactory())->count(5)->create();
// Make instance WITHOUT persisting (for unit tests)
$post = (new PostFactory())->make();Testing JSON API Endpoints
app/Models/Post.php
PHP
class PostApiTest extends TestCase
{
public function test_api_returns_paginated_posts(): void
{
(new PostFactory())->count(20)->create();
$response = $this->get('/api/posts?page=1');
$response->assertOk();
$response->assertJson(['meta' => ['per_page' => 15]]);
$response->assertJsonCount(15, 'data');
}
public function test_api_requires_auth_for_store(): void
{
$response = $this->post('/api/posts', [
'title' => 'Test',
'body' => 'Body',
]);
$response->assertStatus(401);
}
public function test_api_creates_post_for_authenticated_user(): void
{
$user = (new UserFactory())->create();
$response = $this->actingAs($user)->post('/api/posts', [
'title' => 'New API Post',
'body' => 'This is the post body content here.',
]);
$response->assertStatus(200);
$response->assertJsonPath('title', 'New API Post');
$this->assertDatabaseHas('posts', ['title' => 'New API Post', 'user_id' => $user->id]);
}
}Running the Test Suite
Terminal • bash
# Run all tests
php vendor/bin/phpunit
# Run a single test file
php vendor/bin/phpunit tests/Feature/PostTest.php
# Run tests matching a filter
php vendor/bin/phpunit --filter test_guests_cannot_create_posts
# Run with verbose output
php vendor/bin/phpunit --testdox