Skip to content
This repository has been archived by the owner on Apr 3, 2023. It is now read-only.

Commit

Permalink
📝 (README and code) Wrote documentation
Browse files Browse the repository at this point in the history
Documented new folders in the tests/ folder in the README file. Documented class EtuUTTApiTestCase (and its methods). Documented test classes
  • Loading branch information
Teddy Roncin committed Feb 3, 2023
1 parent 2c5ec60 commit 0a66013
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 2 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ Once the local server is running, go to [http://127.0.0.1:8000/](http://127.0.0.
| ├── Security/ # Login process and Voters
| └── Util/ # Static classes to centralize simple tasks
├── tests/ # Automated tests (e.g. Unit tests)
| └── Groups/ # Tests for /groups routes
| └── Users/ # Tests for /users routes
├── var/ # Generated files (cache, logs, etc)
├── vendor/ # The third-party dependencies
├── .czrc # Git Commitizen configuration file
Expand Down
103 changes: 101 additions & 2 deletions tests/EtuUTTApiTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,24 @@
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\EntityManager;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Uid\Uuid;

/**
* Base class for all tests of the project.
* It provides some helper methods.
*/
abstract class EtuUTTApiTestCase extends ApiTestCase
{
protected EntityManager $em;
protected User $user;
private array $databaseBackup;

/**
* Initializes $this->em and $this->user.
* $this->user is an admin user. Its login is 'test'. Tests may use it to connect to the API.
* It also purges the database.
*/
protected function setUp(): void
{
$this->em = static::getContainer()->get('doctrine.orm.entity_manager');
Expand All @@ -28,6 +38,12 @@ protected function setUp(): void
$this->user = $this->createUser('test', 'test', 'test', 'ROLE_ADMIN');
}

/**
* Asserts that the received group is the same as the expected one. It should have the layout defined by the {@see Groups} 'group:read:some' in the {@see Group} entity.
*
* @param Group $expected The group that should be received
* @param mixed $actual The group that was received (as an stdClass)
*/
protected static function assertSameGroupReadSome(Group $expected, mixed $actual): void
{
static::assertIsObject($actual);
Expand All @@ -43,6 +59,12 @@ protected static function assertSameGroupReadSome(Group $expected, mixed $actual
static::assertSame($expected->getNumberOfMembers(), $actual->numberOfMembers);
}

/**
* Asserts that the received group is the same as the expected one. It should have the layout defined by the {@see Groups} 'group:read:one' in the {@see Group} entity.
*
* @param Group $expected The group that should be received
* @param mixed $actual The group that was received (as an stdClass)
*/
protected static function assertSameGroupReadOne(Group $expected, mixed $actual): void
{
static::assertIsObject($actual);
Expand All @@ -66,6 +88,12 @@ protected static function assertSameGroupReadOne(Group $expected, mixed $actual)
static::assertSame($expected->getUpdatedAt()->format(\DateTimeInterface::RFC3339), $actual->updatedAt);
}

/**
* Asserts that the received translation is the same as the expected one.
*
* @param Translation $expected The translation that should be received
* @param mixed $actual The translation that was received (as an stdClass)
*/
protected static function assertSameTranslation(Translation $expected, mixed $actual): void
{
static::assertFalse(null === $expected xor null === $actual);
Expand All @@ -80,6 +108,12 @@ protected static function assertSameTranslation(Translation $expected, mixed $ac
static::assertSame($expected->getChinese(), $actual->chinese);
}

/**
* Asserts that the received user is the same as the expected one. It should have the layout defined by the {@see Groups} 'user:read:some' in the {@see User} entity.
*
* @param User $expected The user that should be received
* @param mixed $actual The user that was received (as an stdClass)
*/
protected static function assertSameUserReadSome(User $expected, mixed $actual): void
{
static::assertIsObject($actual);
Expand All @@ -93,6 +127,12 @@ protected static function assertSameUserReadSome(User $expected, mixed $actual):
static::assertSameUserInfosReadSome($expected->getInfos(), $actual->infos);
}

/**
* Asserts that the received user infos are the same as the expected one. It should have the layout defined by the {@see Groups} 'user:read:some' in the {@see UserInfos} entity.
*
* @param UserInfos $expected The user infos that should be received
* @param mixed $actual The user infos that was received (as an stdClass)
*/
protected static function assertSameUserInfosReadSome(UserInfos $expected, mixed $actual): void
{
static::assertIsObject($actual);
Expand All @@ -103,6 +143,11 @@ protected static function assertSameUserInfosReadSome(UserInfos $expected, mixed
static::assertSame($expected->getNickname(), $actual->nickname);
}

/**
* Loads the given fixtures.
*
* @param Fixture ...$fixtures The fixtures to load
*/
protected function loadFixtures(Fixture ...$fixtures)
{
$fixtureLoader = new Loader();
Expand All @@ -114,6 +159,15 @@ protected function loadFixtures(Fixture ...$fixtures)
}
}

/**
* Creates, persists, and returns a user. It can also flush the database if specified.
*
* @param string $firstName The first name of the user
* @param string $lastName The last name of the user
* @param string $login The login of the user
* @param null|string $role The role of the user (defaults to 'ROLE_USER')
* @param bool $flush Whether to flush the database or not (defaults to true)
*/
protected function createUser(string $firstName, string $lastName, string $login, ?string $role = 'ROLE_USER', bool $flush = true): User
{
$user = new User();
Expand All @@ -129,12 +183,44 @@ protected function createUser(string $firstName, string $lastName, string $login
return $user;
}

/**
* Backups the database. It sets the value of {@see $databaseBackup}. It should be used before the database is altered.
* After changes, tests should use {@see assertDatabaseSameExcept} to assert that only specified fields have been modified.
*/
protected function backupDatabase(): void
{
$this->databaseBackup = [];
$this->_backupDatabase($this->databaseBackup);
}

/**
* Asserts that the database is the same as the backup, except for the specified fields.
*
* @param array $diff The fields that should be different from the backup. It should be an array of the form:
*
* <code>
* [
* '<table_name>' => [
* 'where' => [
* '<field>' => '<value>', // Changes are only expected where <field> have value <value>
* ],
* 'diff' => [
* '<modified_field>' => '<new_value>',
* ],
* ],
* ]
* </code>
* @param array $new The new entries that should be in the database. It should be an array of the form:
*
* <code>
* [
* '<table_name>' => [
* [
* '<field>' => '<value>',
* ], // Each array is a new entry
* ],
* </code>
*/
protected function assertDatabaseSameExcept(array $diff, array $new): void
{
$actualDatabase = [];
Expand All @@ -153,18 +239,31 @@ protected function assertDatabaseSameExcept(array $diff, array $new): void
$this->databaseBackup[$table][] = $entry;
}
}
static::assertEquals($this->databaseBackup, $actualDatabase);
// Yes, that's dumb, but PHPFixer keeps changing it to assertSame
\call_user_func_array([static::class, 'assertEquals'], [$this->databaseBackup, $actualDatabase]);
}

/**
* Backups the database in the given array.
*
* @param array $backup A reference to the array in which the backup will be stored
*/
private function _backupDatabase(array &$backup): void
{
$backup = [];
// Fetch all tables
$tables = $this->em->getConnection()->createSchemaManager()->listTables();
foreach ($tables as $table) {
$tableName = $table->getName();
$backup[$tableName] = [];
// Fetch all rows
$rows = $this->em->getConnection()->prepare("SELECT * FROM {$tableName}")->executeQuery()->fetchAllAssociative();
$getPrintableValue = fn (string $column, $value): ?string => UuidType::class === $table->getColumn($column)->getType()::class && null !== $value ? Uuid::fromBinary($value)->jsonSerialize() : $value;
// We don't want to directly use the binary value of the UUIDs, but the more human-readable representation
$getPrintableValue = function (string $column, $value) use ($table): ?string {
$shouldConvert = UuidType::class === $table->getColumn($column)->getType()::class && null !== $value;

return $shouldConvert ? Uuid::fromBinary($value)->jsonSerialize() : $value;
};
foreach ($rows as $row) {
// Convert all values to printable values
foreach ($row as $column => &$value) {
Expand Down
2 changes: 2 additions & 0 deletions tests/Groups/GetGroupFromSlug.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* @internal
*
* @coversNothing
*
* Tests for route GET /groups/{slug}
*/
final class GetGroupFromSlug extends EtuUTTApiTestCase
{
Expand Down
2 changes: 2 additions & 0 deletions tests/Groups/GetGroups.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* @internal
*
* @coversNothing
*
* Tests for route GET /groups
*/
final class GetGroups extends EtuUTTApiTestCase
{
Expand Down
2 changes: 2 additions & 0 deletions tests/Groups/UpdateGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* @internal
*
* @coversNothing
*
* Tests for route PATCH /groups/{slug}
*/
final class UpdateGroup extends EtuUTTApiTestCase
{
Expand Down
2 changes: 2 additions & 0 deletions tests/Users/DeleteUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* @internal
*
* @coversNothing
*
* Tests for route DELETE /users/{id}
*/
final class DeleteUser extends EtuUTTApiTestCase
{
Expand Down
2 changes: 2 additions & 0 deletions tests/Users/GetUserFromId.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* @internal
*
* @coversNothing
*
* Tests for route GET /users/{id}
*/
final class GetUserFromId extends EtuUTTApiTestCase
{
Expand Down
2 changes: 2 additions & 0 deletions tests/Users/GetUsers.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
* @internal
*
* @coversNothing
*
* Tests for route GET /users
*/
final class GetUsers extends EtuUTTApiTestCase
{
Expand Down
2 changes: 2 additions & 0 deletions tests/Users/UpdateUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
* @internal
*
* @coversNothing
*
* Tests for route PATCH /users/{id}
*/
final class UpdateUser extends EtuUTTApiTestCase
{
Expand Down

0 comments on commit 0a66013

Please sign in to comment.