Automated Testing with Symfony and Codeception: A Beginner’s Guide

Automated testing is a critical part of modern software development, allowing developers to catch bugs early and ensure that their code works as expected. Symfony is a popular PHP web framework that provides many built-in tools for testing, while Codeception is a powerful testing framework that can be used with Symfony to write tests more easily and efficiently.
In this blog post, we’ll provide a step-by-step guide on how to set up automated testing with Symfony and Codeception, complete with code examples. We’ll show you how to create a basic test suite, write and run functional and unit tests, use fixtures to create test data, write acceptance tests using Symfony’s Panther browser automation tool, and use test doubles to isolate code under test.
Whether you’re new to automated testing or just looking to improve your testing skills, this blog post will provide a comprehensive guide on how to use Symfony and Codeception to create robust and reliable tests for your PHP web applications. So let’s get started!
Setting up a Symfony project for automated testing
Before we can start writing automated tests for our Symfony project, we need to make sure that our project is set up to support testing. Here’s how to get started:
Step 1: Create a new Symfony project
To create a new Symfony project, you can use the Symfony CLI. Open up a terminal and enter the following command:
1
symfony new my_project_nameReplace my_project_name with the name of your project. This command will create a new Symfony project in a directory with the same name as your project.
Step 2: Install Codeception and its Symfony module
Codeception is a testing framework that can be used to write functional and unit tests for PHP applications. To install Codeception and its Symfony module, we’ll use Composer. Open up a terminal and navigate to the root directory of your Symfony project, then enter the following commands:
1
2
composer require --dev codeception/codeception
composer require --dev codeception/module-symfonyThese commands will install Codeception and the Symfony module into your project’s dev dependencies.
Step 3: Create a basic test suite
Now that we have Codeception and its Symfony module installed, we can create a basic test suite for our project. Enter the following command in your terminal:
1
vendor/bin/codecept bootstrapThis command will create a tests directory in your project’s root directory, along with some basic configuration files for Codeception.
Congratulations, you’ve set up a Symfony project for automated testing with Codeception! In the next section, we’ll take a look at how to write and run tests using Codeception and Symfony’s BrowserKit.
Writing and running functional tests
Functional tests are used to test the behavior of your application’s controllers and views. They simulate user interactions with your application, such as clicking links and submitting forms, and verify that the application responds correctly.
Here’s how to write and run functional tests with Symfony and Codeception:
Step 1: Generate a functional test
To generate a new functional test, enter the following command in your terminal:
1
vendor/bin/codecept generate:functional test_nameReplace test_name with the name of your test. This command will generate a new test file in the tests/functional directory, along with some basic code for testing a Symfony controller.
Step 2: Write a functional test
Open up the test file that was generated in the previous step and modify it to test your own Symfony controller. Here’s an example test that verifies that the homepage of your application loads successfully:
1
2
3
4
5
public function testHomepage()
{
$this->client->request('GET', '/');
$this->assertSame(200, $this->client->getResponse()->getStatusCode());
}In this test, we use Symfony’s BrowserKit to simulate a GET request to the homepage of our application, and we assert that the response code is 200 (which indicates a successful response).
Step 3: Run the functional test
To run the functional test, enter the following command in your terminal:
1
vendor/bin/codecept run functionalThis command will run all of the functional tests in your test suite. If the test passes, you should see output similar to the following:
1
2
3
Functional Tests (1) -----------------------------
✔ Homepage (0.03s)
-------------------------------------------------
Congratulations, you’ve written and run your first functional test with Symfony and Codeception! In the next section, we’ll take a look at how to write and run unit tests.
Writing a unit test for a service in Symfony
In Symfony, a service is a PHP object that performs a specific task or provides a specific functionality. Services are defined in the services.yaml file in the config directory of your Symfony project. They can be used throughout your application, and can also be tested independently of the rest of your application using unit tests.
Here’s an example of how to write a unit test for a service in Symfony:
Step 1: Create a new service
To create a new service, open up the services.yaml file in the config directory of your Symfony project and define a new service with a unique name. For example:
1
2
3
4
# config/services.yaml
services:
my_service:
class: App\Service\MyServiceThis creates a new service called my_service that is an instance of the App\Service\MyService class.
Step 2: Write a unit test for the service
Create a new test file in the tests/Unit/Service directory of your Symfony project, for example MyServiceTest.php. In this file, create a new test case that extends Symfony\Bundle\FrameworkBundle\Test\KernelTestCase. This class provides a convenient way to bootstrap the Symfony kernel in your test case, which makes it easy to access your services.
Here’s an example test that verifies that the my_service service returns the correct output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// tests/Unit/Service/MyServiceTest.php
namespace App\Tests\Unit\Service;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use App\Service\MyService;
class MyServiceTest extends KernelTestCase
{
public function _before()
{
self::bootKernel();
}
public function testMyService()
{
$myService = self::$container->get(MyService::class);
$this->assertEquals(
'expected output',
$myService->doSomething()
);
}
}In this test, we first boot the Symfony kernel, which initializes the container and makes our services available. We then retrieve the my_service service from the container using self::$container->get(MyService::class), and call the doSomething() method on the service. Finally, we assert that the output of the method matches our expected output.
Step 3: Run the unit test
To run the unit test, enter the following command in your terminal:
1
vendor/bin/phpunit tests/Unit/Service/MyServiceTest.phpThis command will run all of the functional tests in your test suite. If the test passes, you should see output similar to the following:
1
2
3
4
5
6
7
Codeception PHP Testing Framework v4.1.24
Powered by PHPUnit 9.5.10 by Sebastian Bergmann and contributors.
Unit Tests (1) ----------------------------------------------------------------------------------------------------------------------------------
✔ MyServiceCest: MyService returns expected output (0.00s)
--------------------------------------------------------------------------------------------------------------------------------------------------
Time: 00:00.006, Memory: 6.00 MB
OK (1 test, 1 assertion)This output indicates that the test passed successfully, with 1 test and 1 assertion. If the test had failed, Codeception would have output an error message indicating which assertion failed and what the expected and actual values were.
Advanced topics in automated testing
Fixtures
Fixtures are a common technique used in automated testing to create test data for your application. Fixtures are essentially pre-defined sets of data that can be used to populate your database or other data stores, and they are often used in conjunction with unit tests or functional tests.
By default, Codeception looks for fixture files in a tests/_data directory. You can store your fixture files in this directory to keep them organized and separate from your test files.
To load fixtures from the tests/_data directory, you can use the codecept_data_dir() method, which returns the full path to the data directory. You can then use this path to construct the relative path to your fixture file. Using the codecept_data_dir() method can help you keep your test files organised and make it easier to load fixtures from the tests/_data directory.
Note that we’re using User1 as the fixture key to grab a specific user from the fixture in our test method. This corresponds to the User1 key we defined in our fixture file.
1
2
3
4
5
6
7
8
9
10
# tests/_data/fixtures/users.yml
User1:
name: John Doe
email: john.doe@example.com
password: $2y$13$Zjk2OWMwNjI2MTY4OWM4ZO4TA4MDczJh9AjjKtO8ul1C0wHpgpyqi # hashed password
User2:
name: Jane Doe
email: jane.doe@example.com
password: $2y$13$Zjk2OWMwNjI2MTY4OWM4ZO4TA4MDczJh9AjjKtO8ul1C0wHpgpyqi # hashed password
# ...In this example, we’re defining two users in our fixture, User1 and User2. We’re providing some basic data for each user, including a name, email, and a hashed password.
You can define as many users as you need in your fixture, and you can include additional data as well. Fixtures can be a powerful way to create realistic test data that accurately reflects the data you’ll be working with in production.
We can then use these fixtures in our tests with the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
use Codeception\Test\Unit;
class MyServiceTest extends Unit
{
/**
* @var \UnitTester
*/
protected $tester;
public function _before()
{
// Load fixtures
$this->tester->loadFixtures(
[
'users' => codecept_data_dir() . 'fixtures/users.yml',
]
);
}
public f...