Hello!
I have written a PostUpdater service in Slim as follows:
<?php
declare(strict_types=1);
namespace App\Domain\Post\Service;
use App\Domain\Post\Data\Post;
use App\Domain\Post\Repository\PostUpdaterRepository;
use App\Factory\LoggerFactory;
use App\Support\Slug;
use Cake\Validation\Validator;
use Error;
use Exception;
use Psr\Log\LoggerInterface;
use Selective\ArrayReader\ArrayReader;
final class PostUpdater
{
/**
* @Injection
* @var LoggerInterface
*/
private LoggerInterface $logger;
/**
* @Injection
* @var PostUpdaterRepository
*/
private PostUpdaterRepository $repository;
/**
* @Injection
* @var Validator
*/
private Validator $validator;
/**
* The constructor.
*
* @param LoggerFactory $loggerFactory Monolog logger factory.
* @param PostUpdaterRepository $repository Post updater repository
* @param Validator $validator CakePHP validator
*/
public function __construct(
LoggerFactory $loggerFactory,
PostUpdaterRepository $repository,
Validator $validator
) {
$this->logger = $loggerFactory->addFileHandler('error.log')->createLogger();
$this->repository = $repository;
$this->validator = $validator;
}
/**
* Update post entry.
*
* @param array<string> $formData The form data
*
* @return bool
*/
public function update(array $formData): bool
{
$this->validate($formData);
$post = $this->setPost($formData);
try {
$this->repository->update($post);
return true;
} catch (Exception $e) {
$this->logger->error(sprintf("PostFinder->update(): %s", $e->getMessage()));
return false;
} catch (Error $e) {
$this->logger->error(sprintf("PostFinder->update(): %s", $e->getMessage()));
return false;
}
}
/**
* Create a slug by a post title.
*
* @param string $title Post title
*
* @return string
*/
private function slug(string $title): string
{
$slug = new Slug('german');
$slugify = $slug->slugify($title);
return $slugify . '.html';
}
/**
* Create a post object from form data.
*
* @param array<string> $formData The form data.
*
* @return Post
*/
private function setPost(array $formData): Post
{
$reader = new ArrayReader($formData);
$id = $reader->findInt('id');
$title = $reader->findString('title');
$intro = $reader->findString('intro');
$content = $reader->findString('content');
$authorId = $reader->findInt('author_id');
$onMainpage = $reader->findBool('on_mainpage');
$isPublished = $reader->findBool('is_published');
$createdAt = $reader->findString('created_at');
$slug = $this->slug($title);
if ($isPublished)
$publishedAt = date('Y-m-d H:i:s');
$updatedAt = date('Y-m-d H:i:s');
$post = new Post(
$id,
$title,
$slug,
$intro,
$content,
$authorId,
$onMainpage,
$publishedAt,
$isPublished,
$createdAt,
$updatedAt
);
return $post;
}
/**
* Validate the form data.
*
* @param array<mixed> $formData The form data.
*
* @return void
*/
public function validate(array $formData): void
{
$this->validator
->requirePresence('title')
->notEmptyString('title', 'Der Titel darf nicht leer sein.');
$errors = $this->validator->validate($formData);
if ($errors) {
foreach ($errors as $error) {
dd($error);
}
}
}
}
Because I keep having problems with these services, I would like to write tests for them. I just don’t know exactly how and what makes sense.
More precisely, I fail because I can’t instantiate a PostUpdater object because it requires several parameters. LoggerFactory, PostUpdaterRepository, Validator.
I got my first ideas with Copilot:
<?php
declare(strict_types=1);
namespace Tests\Domain\Posts\Post;
use App\Domain\Post\Repository\PostUpdaterRepository;
use App\Domain\Post\Service\PostUpdater;
use App\Factory\LoggerFactory;
use Cake\Validation\Validator;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use ReflectionClass;
class PostUpdaterTest extends TestCase
{
private PostUpdaterRepository $repository;
private Validator $validator;
public function setUp(): void
{
$this->repository = $this->createMock(PostUpdaterRepository::class);
$this->validator = $this->createMock(Validator::class);
$logger = $this->createMock(LoggerInterface::class);
$loggerFactory = $this->getMockBuilder(LoggerFactory::class)
->disableOriginalConstructor()
->getMock();
$loggerFactory->method('AddFileHandler')->willReturnSelf();
$loggerFactory->method('createLogger')->willReturn($logger);
$this->postUpdater = new PostUpdater($loggerFactory, $this->repository, $this->validator);
}
...
}
Which doesn’t work because PostUpdater expects a LoggerFactory of type App\Factory\LoggerFactory and gets a PHPUnit\Framework\MockObject\MockObject.
So what is the right way to test such a service?
I would be very grateful for any tips.
With kind regards