first commit
This commit is contained in:
188
tests/Category/CategoryServiceTest.php
Normal file
188
tests/Category/CategoryServiceTest.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Category;
|
||||
|
||||
use App\Category\Category;
|
||||
use App\Category\CategoryRepositoryInterface;
|
||||
use App\Category\CategoryService;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Tests unitaires pour CategoryService.
|
||||
*
|
||||
* Vérifie la création (génération de slug, unicité du nom, validation du modèle)
|
||||
* et la suppression (blocage si articles rattachés).
|
||||
* Le repository est remplacé par un mock pour isoler le service.
|
||||
*/
|
||||
final class CategoryServiceTest extends TestCase
|
||||
{
|
||||
/** @var CategoryRepositoryInterface&MockObject */
|
||||
private CategoryRepositoryInterface $repository;
|
||||
|
||||
private CategoryService $service;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->repository = $this->createMock(CategoryRepositoryInterface::class);
|
||||
$this->service = new CategoryService($this->repository);
|
||||
}
|
||||
|
||||
|
||||
// ── create ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* create() doit générer le slug depuis le nom et persister la catégorie.
|
||||
*/
|
||||
public function testCreateGeneratesSlugAndPersists(): void
|
||||
{
|
||||
$this->repository->method('nameExists')->willReturn(false);
|
||||
$this->repository->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->callback(fn (Category $c) =>
|
||||
$c->getName() === 'Développement web'
|
||||
&& $c->getSlug() === 'developpement-web'
|
||||
))
|
||||
->willReturn(1);
|
||||
|
||||
$id = $this->service->create('Développement web');
|
||||
|
||||
$this->assertSame(1, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* create() doit trimmer le nom avant de générer le slug.
|
||||
*/
|
||||
public function testCreateTrimsName(): void
|
||||
{
|
||||
$this->repository->method('nameExists')->willReturn(false);
|
||||
$this->repository->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->callback(fn (Category $c) => $c->getName() === 'PHP'))
|
||||
->willReturn(2);
|
||||
|
||||
$this->service->create(' PHP ');
|
||||
}
|
||||
|
||||
/**
|
||||
* create() doit lever InvalidArgumentException si le slug généré est vide.
|
||||
*/
|
||||
public function testCreateNonAsciiNameThrowsException(): void
|
||||
{
|
||||
$this->repository->expects($this->never())->method('create');
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('slug URL valide');
|
||||
|
||||
$this->service->create('日本語');
|
||||
}
|
||||
|
||||
/**
|
||||
* create() doit lever InvalidArgumentException si le nom est déjà utilisé.
|
||||
*/
|
||||
public function testCreateDuplicateNameThrowsException(): void
|
||||
{
|
||||
$this->repository->method('nameExists')->willReturn(true);
|
||||
$this->repository->expects($this->never())->method('create');
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('déjà utilisé');
|
||||
|
||||
$this->service->create('PHP');
|
||||
}
|
||||
|
||||
/**
|
||||
* create() doit lever InvalidArgumentException si le nom est vide.
|
||||
*/
|
||||
public function testCreateEmptyNameThrowsException(): void
|
||||
{
|
||||
$this->repository->method('nameExists')->willReturn(false);
|
||||
$this->repository->expects($this->never())->method('create');
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
|
||||
$this->service->create('');
|
||||
}
|
||||
|
||||
/**
|
||||
* create() doit lever InvalidArgumentException si le nom dépasse 100 caractères.
|
||||
*/
|
||||
public function testCreateNameTooLongThrowsException(): void
|
||||
{
|
||||
$longName = str_repeat('a', 101);
|
||||
$this->repository->method('nameExists')->willReturn(false);
|
||||
$this->repository->expects($this->never())->method('create');
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
|
||||
$this->service->create($longName);
|
||||
}
|
||||
|
||||
|
||||
// ── delete ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* delete() doit supprimer la catégorie si elle ne contient aucun article.
|
||||
*/
|
||||
public function testDeleteSucceedsWhenNoPosts(): void
|
||||
{
|
||||
$category = new Category(5, 'PHP', 'php');
|
||||
|
||||
$this->repository->method('hasPost')->with(5)->willReturn(false);
|
||||
$this->repository->expects($this->once())->method('delete')->with(5);
|
||||
|
||||
$this->service->delete($category);
|
||||
}
|
||||
|
||||
/**
|
||||
* delete() doit lever InvalidArgumentException si des articles sont rattachés.
|
||||
*/
|
||||
public function testDeleteBlockedWhenPostsAttached(): void
|
||||
{
|
||||
$category = new Category(5, 'PHP', 'php');
|
||||
|
||||
$this->repository->method('hasPost')->with(5)->willReturn(true);
|
||||
$this->repository->expects($this->never())->method('delete');
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('contient des articles');
|
||||
|
||||
$this->service->delete($category);
|
||||
}
|
||||
|
||||
|
||||
// ── Lectures déléguées ─────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* findAll() doit déléguer au repository et retourner son résultat.
|
||||
*/
|
||||
public function testFindAllDelegatesToRepository(): void
|
||||
{
|
||||
$cats = [new Category(1, 'PHP', 'php'), new Category(2, 'CSS', 'css')];
|
||||
$this->repository->method('findAll')->willReturn($cats);
|
||||
|
||||
$this->assertSame($cats, $this->service->findAll());
|
||||
}
|
||||
|
||||
/**
|
||||
* findById() doit retourner null si la catégorie n'existe pas.
|
||||
*/
|
||||
public function testFindByIdReturnsNullWhenMissing(): void
|
||||
{
|
||||
$this->repository->method('findById')->willReturn(null);
|
||||
|
||||
$this->assertNull($this->service->findById(99));
|
||||
}
|
||||
|
||||
/**
|
||||
* findBySlug() doit retourner la catégorie correspondante.
|
||||
*/
|
||||
public function testFindBySlugReturnsCategoryWhenFound(): void
|
||||
{
|
||||
$cat = new Category(3, 'PHP', 'php');
|
||||
$this->repository->method('findBySlug')->with('php')->willReturn($cat);
|
||||
|
||||
$this->assertSame($cat, $this->service->findBySlug('php'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user