# Symfony'de Test 2026: PHPUnit, KernelTestCase ve Fonksiyonel Testler > PHPUnit 12, KernelTestCase ve WebTestCase ile Symfony test stratejileri. Birim, entegrasyon ve fonksiyonel testler icin kapsamli kilavuz. - Published: 2026-07-15 - Updated: 2026-07-15 - Author: SharpSkill - Reading time: 5 min --- PHPUnit 12 ile Symfony uygulamalarini test etmek, birim testlerinden tam HTTP istek simulasyonlarina kadar uygulama davranisini dogrulama konusunda yapilandirilmis bir yaklasim sunar. Framework'un test altyapisi servis konteyneri ile sikica entegre olur ve bu sayede servisler, controller'lar ve veritabani islemlerinin minimum tekrarlayan kodla test edilmesi kolaylasir. > **Symfony 7.3'te Test Stack** > > Symfony 7.3, PHPUnit 12 destegi, `ResetInterface` ile gelistirilmis test izolasyonu ve fonksiyonel test assertion'larini daha okunakli hale getiren `CrawlerSelector` bileseni ile birlikte gelir. `WebTestCase` artik HTTP/2 push assertion'larini kutusundan cikar cikmaz destekler. ## PHPUnit 12 ile Servis Birim Testleri Birim testleri, tek tek siniflari izole bir sekilde dogrular. Konteyner'a bagimliligi olmayan Symfony servisleri icin standart PHPUnit testleri herhangi bir framework katilimi olmadan calisir. ```php // tests/Unit/Service/PriceCalculatorTest.php namespace App\Tests\Unit\Service; use App\Service\PriceCalculator; use PHPUnit\Framework\TestCase; final class PriceCalculatorTest extends TestCase { private PriceCalculator $calculator; protected function setUp(): void { // Create instance directly - no container needed $this->calculator = new PriceCalculator(vatRate: 0.20); } public function testCalculateTotalWithVat(): void { $result = $this->calculator->calculateTotal(100.00); // Assert exact decimal value with delta for float comparison $this->assertEqualsWithDelta(120.00, $result, 0.001); } public function testCalculateTotalWithZeroAmount(): void { $result = $this->calculator->calculateTotal(0.00); $this->assertEqualsWithDelta(0.00, $result, 0.001); } } ``` Saf birim testleri entegrasyon testlerinden daha hizli calisir ve hatalari tam olarak belirler. Bu testler, veri donusturmesi yapan veya hesaplamalar gerceklestiren is mantigi siniflari icin kullanilmalidir. ## KernelTestCase ile Entegrasyon Testleri Servisler Symfony konteynerine bagimli oldugunda—veritabani baglantilari, cache adapterleri veya diger servisler—`KernelTestCase` minimal bir kernel baslatir ve servis konteynerine erisim saglar. ```php // tests/Integration/Repository/UserRepositoryTest.php namespace App\Tests\Integration\Repository; use App\Entity\User; use App\Repository\UserRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; final class UserRepositoryTest extends KernelTestCase { private EntityManagerInterface $em; private UserRepository $repository; protected function setUp(): void { // Boot kernel and fetch services from container self::bootKernel(); $container = static::getContainer(); $this->em = $container->get(EntityManagerInterface::class); $this->repository = $container->get(UserRepository::class); } public function testFindByEmailReturnsUserWhenExists(): void { // Arrange: create and persist test user $user = new User(); $user->setEmail('test@example.com'); $user->setPassword('hashed_password'); $this->em->persist($user); $this->em->flush(); // Act: query through repository $found = $this->repository->findByEmail('test@example.com'); // Assert: verify retrieval $this->assertNotNull($found); $this->assertSame('test@example.com', $found->getEmail()); } protected function tearDown(): void { // Clean up to prevent test pollution $this->em->createQuery('DELETE FROM App\Entity\User')->execute(); parent::tearDown(); } } ``` `getContainer()` metodu, test amacli olarak ozel servisleri acan test konteynerini dondurur. Bu, ozel servislerin erisime kapali kaldigi calisma zamani konteynerinden farklidir. ## Transaction'larla Veritabani Izolasyonu Test izolasyonu, bir testteki verilerin digerini etkilemesini onler. [DAMADoctrineTestBundle](https://github.com/dmaicher/doctrine-test-bundle) her testi otomatik olarak geri alinan bir transaction icine sarar. ```php // tests/Integration/Service/OrderServiceTest.php namespace App\Tests\Integration\Service; use App\Entity\Order; use App\Service\OrderService; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; final class OrderServiceTest extends KernelTestCase { private OrderService $orderService; private EntityManagerInterface $em; protected function setUp(): void { self::bootKernel(); $container = static::getContainer(); $this->orderService = $container->get(OrderService::class); $this->em = $container->get(EntityManagerInterface::class); } public function testCreateOrderPersistsToDatabase(): void { // With DAMA bundle, this persists within a transaction $order = $this->orderService->create( customerId: 1, items: [['sku' => 'ABC123', 'quantity' => 2]] ); // Verify persistence $this->em->clear(); $persisted = $this->em->find(Order::class, $order->getId()); $this->assertNotNull($persisted); $this->assertCount(1, $persisted->getItems()); } // Transaction rolls back after test - no cleanup needed } ``` Bundle, `phpunit.xml.dist` dosyasina extension eklenerek yapilandirilir. Boylece her test, manuel temizleme kodu olmadan tam izolasyonda calisir. ## WebTestCase ile Fonksiyonel Testler Fonksiyonel testler HTTP isteklerini simule eder ve yanitlari dogrular. `WebTestCase`, web sunucusu baslatmadan controller'lara istek yapan bir istemci saglar. ```php // tests/Functional/Controller/ProductControllerTest.php namespace App\Tests\Functional\Controller; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\HttpFoundation\Response; final class ProductControllerTest extends WebTestCase { public function testListProductsReturnsJsonArray(): void { // Create client and make GET request $client = static::createClient(); $client->request('GET', '/api/products'); // Assert response status $this->assertResponseIsSuccessful(); $this->assertResponseStatusCodeSame(Response::HTTP_OK); // Assert JSON structure $this->assertResponseHeaderSame('Content-Type', 'application/json'); $data = json_decode($client->getResponse()->getContent(), true); $this->assertIsArray($data); $this->assertArrayHasKey('products', $data); } public function testCreateProductRequiresAuthentication(): void { $client = static::createClient(); $client->request('POST', '/api/products', [], [], [ 'CONTENT_TYPE' => 'application/json', ], json_encode(['name' => 'New Product', 'price' => 29.99])); // Unauthenticated requests should return 401 $this->assertResponseStatusCodeSame(Response::HTTP_UNAUTHORIZED); } } ``` `$client->request()` metodu HTTP metodu, URI, parametreler, dosyalar, sunucu degiskenleri ve body icerigini kabul eder. Symfony istegi middleware, guvenlik ve routing dahil tam kernel uzerinden isler. ## Kimlik Dogrulanmis Istekleri Test Etme Kimlik dogrulama gerektiren endpoint'ler icin [Symfony'nin guvenlik bileseni](https://symfony.com/doc/current/security.html) test istemcisinde `loginUser()` metodunu saglar. ```php // tests/Functional/Controller/AdminControllerTest.php namespace App\Tests\Functional\Controller; use App\Entity\User; use App\Repository\UserRepository; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; final class AdminControllerTest extends WebTestCase { public function testAdminDashboardRequiresAdminRole(): void { $client = static::createClient(); $container = static::getContainer(); // Fetch a test user with ROLE_ADMIN $userRepository = $container->get(UserRepository::class); $adminUser = $userRepository->findOneBy(['email' => 'admin@example.com']); // Authenticate the client $client->loginUser($adminUser); // Access protected route $client->request('GET', '/admin/dashboard'); $this->assertResponseIsSuccessful(); $this->assertSelectorTextContains('h1', 'Admin Dashboard'); } public function testRegularUserCannotAccessAdmin(): void { $client = static::createClient(); $container = static::getContainer(); $userRepository = $container->get(UserRepository::class); $regularUser = $userRepository->findOneBy(['email' => 'user@example.com']); $client->loginUser($regularUser); $client->request('GET', '/admin/dashboard'); // Should be forbidden, not redirected to login $this->assertResponseStatusCodeSame(403); } } ``` `loginUser()` metodu, giris formundan gecmeden guvenlik token'ini ayarlar, bu da testleri daha hizli ve odakli hale getirir. ## Form Testi ve CSRF Yonetimi Form testi CSRF token'larinin islenmesini gerektirir. Crawler bileseni form elemanlarini cikarir ve uygun token yonetimiyle gonderir. ```php // tests/Functional/Controller/RegistrationControllerTest.php namespace App\Tests\Functional\Controller; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; final class RegistrationControllerTest extends WebTestCase { public function testRegistrationFormSubmission(): void { $client = static::createClient(); // Load the registration page $crawler = $client->request('GET', '/register'); // Extract the form and fill fields $form = $crawler->selectButton('Register')->form([ 'registration_form[email]' => 'newuser@example.com', 'registration_form[plainPassword]' => 'SecurePass123!', 'registration_form[agreeTerms]' => true, ]); // Submit - CSRF token is included automatically $client->submit($form); // Assert redirect after successful registration $this->assertResponseRedirects('/login'); // Follow redirect and verify flash message $client->followRedirect(); $this->assertSelectorTextContains('.alert-success', 'Registration successful'); } public function testRegistrationValidationErrors(): void { $client = static::createClient(); $crawler = $client->request('GET', '/register'); // Submit with invalid email $form = $crawler->selectButton('Register')->form([ 'registration_form[email]' => 'not-an-email', 'registration_form[plainPassword]' => '123', // Too short 'registration_form[agreeTerms]' => false, ]); $client->submit($form); // Should stay on same page with errors $this->assertResponseIsSuccessful(); $this->assertSelectorExists('.invalid-feedback'); } } ``` `selectButton()` metodu formu submit buton metnine gore bulur. Gorunur butonu olmayan formlar icin CSS seciici ile `selectForm()` kullanilabilir. ## Konsol Komutlarini Test Etme Komutlar genellikle veri aktarimlari veya zamanlanmis gorevler gibi kritik islemleri gerceklestirir. `CommandTester` komutlari calistirir ve ciktilarini yakalar. ```php // tests/Command/ImportUsersCommandTest.php namespace App\Tests\Command; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Console\Tester\CommandTester; final class ImportUsersCommandTest extends KernelTestCase { public function testImportUsersWithValidCsv(): void { self::bootKernel(); $application = new Application(self::$kernel); $command = $application->find('app:import-users'); $tester = new CommandTester($command); // Execute with arguments and options $tester->execute([ 'file' => 'tests/fixtures/users.csv', '--dry-run' => true, ]); // Assert exit code (0 = success) $this->assertSame(0, $tester->getStatusCode()); // Assert output contains expected text $output = $tester->getDisplay(); $this->assertStringContainsString('Imported 5 users', $output); } public function testImportUsersFailsWithMissingFile(): void { self::bootKernel(); $application = new Application(self::$kernel); $command = $application->find('app:import-users'); $tester = new CommandTester($command); $tester->execute(['file' => 'nonexistent.csv']); // Non-zero exit code indicates failure $this->assertSame(1, $tester->getStatusCode()); $this->assertStringContainsString('File not found', $tester->getDisplay()); } } ``` Etkilesimli girisi olan komutlar icin `execute()` metoduna ikinci arguman olarak `['interactive' => false]` gecirilebilir veya kullanici yanitlarini simule etmek icin `setInputs()` kullanilabilir. ## Asenkron Gorevler icin Messenger Testi [Symfony Messenger](/technologies/symfony/interview-questions/messenger) handler'larini test etmek, mesajlarin dogru sekilde gonderildigini ve handler'larin beklenildigi gibi islem yaptigini dogrulamayi gerektirir. ```php // tests/Integration/Message/SendWelcomeEmailHandlerTest.php namespace App\Tests\Integration\Message; use App\Message\SendWelcomeEmail; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Transport\InMemoryTransport; final class SendWelcomeEmailHandlerTest extends KernelTestCase { public function testWelcomeEmailIsDispatched(): void { self::bootKernel(); $container = static::getContainer(); $bus = $container->get(MessageBusInterface::class); // Dispatch the message $bus->dispatch(new SendWelcomeEmail(userId: 123)); // Retrieve the in-memory transport configured in config/packages/test/messenger.yaml /** @var InMemoryTransport $transport */ $transport = $container->get('messenger.transport.async'); // Assert message was sent $this->assertCount(1, $transport->getSent()); $envelope = $transport->getSent()[0]; $message = $envelope->getMessage(); $this->assertInstanceOf(SendWelcomeEmail::class, $message); $this->assertSame(123, $message->getUserId()); } } ``` Gonderilen mesajlari gercek kuyruk islemesi olmadan yakalamak icin `config/packages/test/messenger.yaml` dosyasinda bir in-memory transport yapilandirilmalidir. ## Kod Kapsami Yapilandirmasi [PHPUnit kod kapsami](https://docs.phpunit.de/en/12.0/code-coverage.html) raporlari test edilmemis kod yollarini belirler. Kapsam yapilandirmasi `phpunit.xml.dist` dosyasinda yapilir. ```xml tests/Unit tests/Integration tests/Functional src src/DataFixtures src/Migrations ``` Testler `php bin/phpunit --coverage-html var/coverage` komutuyla kapsamla birlikte calistirilir. HTML raporu her satirin kapsam durumunu gosterir. ## Sonuc - Konteyner bagimliligi olmayan saf birim testleri icin `TestCase` kullanilir - Testler konteynerden servis gerektirdiginde `KernelTestCase` kullanilir - HTTP istek simulasyonu ve yanit assertion'lari icin `WebTestCase` kullanilir - Testler arasinda otomatik transaction rollback icin [DAMADoctrineTestBundle](https://github.com/dmaicher/doctrine-test-bundle) yapilandirilir - Giris formlarini simule etmek yerine `loginUser()` ile kimlik dogrulama test edilir - Giris/cikis yakalama ile konsol komutu dogrulamasi icin `CommandTester` kullanilir - Messenger testi icin test ortaminda in-memory transport yapilandirilir - Testler netlik icin Unit, Integration ve Functional dizinlerine duzenlenir --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/symfony/symfony-testing-phpunit-kerneltestcase-functional-tests