Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Writing Testable Symfony Apps

Writing Testable Symfony Apps

Do you struggle as soon as you want to test a controller or a repository? Do you feel like it's sometimes impossible to write unit tests for a Symfony application? In this talk, I will show you design patterns that will not only make testing your Symfony applications trivial, but will also make your code a pleasure to work with.

Anna Filina

June 16, 2022
Tweet

More Decks by Anna Filina

Other Decks in Programming

Transcript

  1. Anna Filina • Coding since 1997 • PHP since 2003

    • Legacy archaeology • Test automation • Public speaking • Mentorship • YouTube videos
  2. class MyController extends AbstractController { public function index(): Response {

    return $this->render('my/index.html.twig', [ 'variable' => 'value', ]); } }
  3. class MyControllerTest extends TestCase { public function testIndex(): void {

    $controller = new MyController(); $controller->index(); } }
  4. Error : Call to a member function has() on null

    ./vendor/symfony/framework-bundle/Controller/AbstractController.php:254 ./vendor/symfony/framework-bundle/Controller/AbstractController.php:266 ./src/Controller/MyController.php:14 ./tests/Controller/MyControllerTest.php:25
  5. class MyController extends AbstractController { public function index(): Response {

    return $this->render('my/index.html.twig', [ 'variable' => 'value', ]); } }
  6. use Twig\Environment; class MyTestableController { public function __construct(private Environment $twig)

    { } public function index(): Response { $html = $this->twig->render('my/index.html.twig', [ 'var' => 'value', ]); return new Response($html); } }
  7. public function testIndex(): void { $controller = new MyTestableController( $twig

    = $this->createMock(Environment::class) ); $twig ->method('render') ->with('my/index.html.twig', ['var' => 'value']) ->willReturn('some content'); self::assertEquals( 'some content', $controller->index()->getContent() ); }
  8. # Behat feature file Scenario: Buyer can purchase using a

    credit card Given I selected a product When I submit a valid credit card Then I should see a payment receipt afilina.com/learn
  9. final class ChargeRequest { public function __construct( readonly public string

    $token, readonly public int $amount, readonly public string $description ) {} }
  10. final class ChargeRequest { public function __construct( readonly public string

    $token, readonly public int $amount, readonly public string $description ) { if ($amount < 0) { throw new \DomainException('Amount cannot be lower than 0'); } } }
  11. • Avoid extending framework classes • Use multiple test types

    in tandem (pyramid) • Extract business logic from infrastructure • Use small immutable classes