I’m encountering an issue with my PHPUnit paratest configuration… or maybe my CI, Docker, or even my code — I’m not sure.
I have some tests for my Symfony web application, and I’m facing a problem where the user does not remain logged in. When I log in the user using $this->client->login()
, no errors occur, and the assertion following the login confirms success. However, as soon as I make a subsequent request, the user appears to be logged out and is redirected to the login page.
If this were happening with every request, I’d have a better idea of where to start troubleshooting, but it only occurs intermittently. Additionally, this issue only happens in the GitHub Actions runner. When I run the tests locally, everything works fine — whether I use paratest or plain PHPUnit.
However, I recently discovered that if I run the tests sequentially on CI, everything works as expected.
Has anyone experienced this issue or know of a fix?
Here is the section of my checks.yml
which triggers the test
tests:
name: Tests
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Docker Compose Up
run: docker compose up -d
- name: Wait for services
run: sleep 30
- name: Setup Test Environment
run: |
docker compose exec app bash -c "
rm -rf var/cache/*
mkdir -p var/cache/test
chmod -R 777 var/
composer dump-autoload
php bin/console cache:clear --env=test
APP_ENV=test php bin/console cache:warmup
chmod -R 777 var/
"
- name: Paratest
run: |
docker compose exec app bash -c "
APP_ENV=test vendor/bin/paratest --configuration=phpunit.xml.dist --runner=WrapperRunner --testdox --verbose
vendor/bin/phpunit --configuration=phpunit.xml.dist
"
- name: Docker Compose Down
if: always()
run: docker compose down
And here is an example test, here the test testActive
always fails…
<?php
namespace AppTestsController;
use AppEntityTutorProfile;
use AppEntityUser;
use AppFactoryPermissionFactory;
use AppFactoryStudentProfileFactory;
use AppFactoryTutorProfileFactory;
use AppFactoryUserFactory;
use DoctrineORMEntityManagerInterface;
use SymfonyBundleFrameworkBundleKernelBrowser;
use SymfonyBundleFrameworkBundleTestWebTestCase;
use ZenstruckFoundryTestFactories;
use ZenstruckFoundryTestResetDatabase;
final class StudentControllerTest extends WebTestCase
{
use Factories;
use ResetDatabase;
private KernelBrowser $client;
private EntityManagerInterface $entityManager;
private User $user;
private TutorProfile $profile;
protected function setUp(): void
{
$this->client = static::createClient();
$this->entityManager = static::getContainer()->get(EntityManagerInterface::class);
$this->user = UserFactory::createOne()->_real();
$this->profile = TutorProfileFactory::createOne(['user_entity' => $this->user])->_real();
$permission = PermissionFactory::createOne(['name' => 'CAN_VIEW_STUDENT_LIST'])->_real();
$this->profile->addPermission($permission);
$this->entityManager->flush();
$this->client->loginUser($this->user);
$this->client->request('GET', '/select-profile');
$this->client->followRedirects();
}
public function testActive(): void
{
StudentProfileFactory::createMany(5, [
'archived' => false,
'deactivated' => false,
'school' => $this->profile->getSchool(),
]);
$this->client->request('GET', '/students');
$this->assertResponseIsSuccessful();
$this->assertSelectorTextContains('body > div.flex.min-h-screen > div.flex-1.flex.flex-col > main > div > h2', 'Schülerliste');
$this->assertCount(5, $this->client->getCrawler()->filter('tbody tr'));
}
public function testArchived(): void
{
StudentProfileFactory::createMany(5, [
'archived' => true,
'deactivated' => false,
'school' => $this->profile->getSchool(),
]);
$this->client->request('GET', '/students/archived');
$this->assertResponseIsSuccessful();
$this->assertSelectorTextContains('body > div.flex.min-h-screen > div.flex-1.flex.flex-col > main > div > h2', 'Schülerliste');
$this->assertCount(5, $this->client->getCrawler()->filter('tbody tr'));
}
public function testDeactivated(): void
{
StudentProfileFactory::createMany(5, [
'archived' => false,
'deactivated' => true,
'school' => $this->profile->getSchool(),
]);
$this->client->request('GET', '/students/deactivated');
$this->assertResponseIsSuccessful();
$this->assertSelectorTextContains('body > div.flex.min-h-screen > div.flex-1.flex.flex-col > main > div > h2', 'Schülerliste');
}
# ...
}
And here is the error:
1) AppTestsControllerStudentControllerTest::testActive
Failed asserting that SymfonyComponentDomCrawlerCrawler Object &0000000000000bf90000000000000000 (
'defaultNamespacePrefix' => 'default'
'namespaces' => Array &0 ()
'cachedNamespaces' => ArrayObject Object &0000000000000f200000000000000000 ()
'baseHref' => 'http://localhost/login'
'document' => DOMDocument Object &0000000000000f240000000000000000 ()
'nodes' => Array &1 (
0 => DOMElement Object &0000000000000f220000000000000000 (
'schemaTypeInfo' => null
)
)
'isHtml' => true
'html5Parser' => MastermindsHTML5 Object &0000000000000aab0000000000000000 (
'defaultOptions' => Array &2 (
'encode_entities' => false
'disable_html_ns' => true
)
'errors' => Array &3 ()
)
'uri' => 'http://localhost/login'
) matches selector "body > div.flex.min-h-screen > div.flex-1.flex.flex-col > main > div > h2" and the Crawler has a node matching selector "body > div.flex.min-h-screen > div.flex-1.flex.flex-col > main > div > h2".
/var/www/html/vendor/symfony/framework-bundle/Test/DomCrawlerAssertionsTrait.php:44
/var/www/html/tests/Controller/StudentControllerTest.php:54