This guide covers how to run tests and write new tests for PHP Builder Generator.
Make sure you have the development dependencies installed:
composer install
composer test
Or directly with PHPUnit:
./vendor/bin/phpunit
Run only unit tests:
./vendor/bin/phpunit --testsuite=unit
Run only integration tests:
./vendor/bin/phpunit --testsuite=integration
./vendor/bin/phpunit --coverage-html coverage/
This generates an HTML coverage report in the coverage/
directory.
Tests are organized in the tests/
directory:
tests/
├── Unit/ # Unit tests
├── Integration/ # Integration tests
├── Fixtures/ # Test fixtures and sample classes
└── TestCase.php # Base test class
Unit tests should focus on testing individual components in isolation:
<?php
namespace MaxBeckers\PhpBuilderGenerator\Tests\Unit\Parser;
use MaxBeckers\PhpBuilderGenerator\Parser\ClassParser;
use MaxBeckers\PhpBuilderGenerator\Tests\TestCase;
class ClassParserTest extends TestCase
{
private ClassParser $parser;
protected function setUp(): void
{
parent::setUp();
$this->parser = new ClassParser();
}
public function testParseSimpleClass(): void
{
$code = '<?php class User { public string $name; }';
$result = $this->parser->parse($code);
$this->assertNotNull($result);
$this->assertEquals('User', $result->getName());
$this->assertCount(1, $result->getProperties());
}
}
Integration tests verify that components work together correctly:
<?php
namespace MaxBeckers\PhpBuilderGenerator\Tests\Integration\Generation;
use MaxBeckers\PhpBuilderGenerator\Tests\TestCase;
class BuilderGenerationTest extends TestCase
{
public function testGenerateBuilderForSimpleClass(): void
{
$this->copyFixtureToTempDir('SimpleUser.php', 'src/');
$this->runCommand('generate');
$this->assertFileExists($this->getTempPath('generated/SimpleUserBuilder.php'));
$generatedCode = file_get_contents($this->getTempPath('generated/SimpleUserBuilder.php'));
$this->assertStringContainsString('class SimpleUserBuilder', $generatedCode);
$this->assertStringContainsString('public function setName(string $name): self', $generatedCode);
}
}
For testing generated builders, create fixtures and verify the output:
public function testGeneratedBuilderWorks(): void
{
$this->generateBuilderFor(UserFixture::class);
// Include the generated file
require_once $this->getGeneratedPath('UserBuilder.php');
$user = UserBuilder::builder()
->name('Test User')
->email('test@example.com')
->build();
$this->assertEquals('Test User', $user->name);
$this->assertEquals('test@example.com', $user->email);
}
Create sample classes in tests/Fixtures/Classes/
:
<?php
// tests/Fixtures/Classes/UserFixture.php
namespace MaxBeckers\PhpBuilderGenerator\Tests\Fixtures\Classes;
use MaxBeckers\PhpBuilderGenerator\Attributes\Builder;
#[Builder]
class UserFixture
{
public function __construct(
public string $name,
public string $email,
public ?int $age = null
) {}
}
Create expected generated code in tests/Fixtures/Generated/
:
<?php
// tests/Fixtures/Generated/UserFixtureBuilder.php
namespace MaxBeckers\PhpBuilderGenerator\Tests\Fixtures\Classes;
class UserFixtureBuilder
{
private string $name;
private string $email;
private ?int $age = null;
public static function builder(): self
{
return new self();
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
// ... rest of the builder
}
Test CLI commands using Symfony Console’s testing utilities:
use Symfony\Component\Console\Tester\CommandTester;
public function testGenerateCommand(): void
{
$command = new GenerateCommand();
$commandTester = new CommandTester($command);
$commandTester->execute([
'--src-dirs' => 'tests/Fixtures/Classes',
'--output-dir' => $this->getTempDir()
]);
$this->assertEquals(0, $commandTester->getStatusCode());
$this->assertStringContainsString('Generated', $commandTester->getDisplay());
}
Test configuration parsing and validation:
public function testConfigurationParsing(): void
{
$config = [
'src-dirs' => ['src', 'app'],
'output-dir' => 'generated/',
'namespace-suffix' => '\\Builder'
];
$parsedConfig = $this->configParser->parse($config);
$this->assertEquals(['src', 'app'], $parsedConfig->getSourceDirs());
$this->assertEquals('generated/', $parsedConfig->getOutputDir());
$this->assertEquals('\\Builder', $parsedConfig->getNamespaceSuffix());
}
Use virtual file system for file operations:
use org\bovigo\vfs\vfsStream;
public function setUp(): void
{
$this->fileSystem = vfsStream::setup('root', null, [
'src' => [
'User.php' => '<?php class User {}'
]
]);
}
public function testFileGeneration(): void
{
$generator = new BuilderGenerator($this->fileSystem->url());
$generator->generateFor($this->userClass);
$this->assertTrue($this->fileSystem->hasChild('generated/UserBuilder.php'));
}
Test generation performance with large numbers of classes:
public function testGenerationPerformance(): void
{
$startTime = microtime(true);
for ($i = 0; $i < 100; $i++) {
$this->generator->generate($this->createTestClass("Class$i"));
}
$endTime = microtime(true);
$duration = $endTime - $startTime;
$this->assertLessThan(5.0, $duration, 'Generation took too long');
}
public function testBuilderGeneration(): void
{
// Arrange
$class = $this->createTestClass();
$generator = new BuilderGenerator();
// Act
$result = $generator->generateFor($class);
// Assert
$this->assertInstanceOf(GeneratedBuilder::class, $result);
}
// Good
public function testGenerateBuilderForClassWithConstructorParameters(): void
// Bad
public function testGenerate(): void
Each test should focus on one specific behavior or scenario.
/**
* @dataProvider typeProvider
*/
public function testPropertyTypes(string $phpType, string $expectedType): void
{
$property = $this->createProperty($phpType);
$result = $this->typeResolver->resolve($property);
$this->assertEquals($expectedType, $result);
}
public function typeProvider(): array
{
return [
['string', 'string'],
['?int', '?int'],
['array', 'array'],
['User', 'User']
];
}
Tests run automatically on:
The CI configuration includes:
Always run the full test suite before pushing:
composer test
Fix any failing tests and ensure coverage remains high.
Next: Development Setup Guide