I'm trying to write a PHP UnitTest for my AddHandler::class
in Mezzio (Zend Expressive) but I'm not sure I've done it right or wrong. Although the Test passes but I'm not really convinced that's the way to do it. The requirement is to basically mock the output of service (new CrmApiService())->getUsers()
and (new CustomHydrator())->getHydrated($this->usersJson)
which can be saved in a text file for that matter. I've another one ViewHandler::class
which also uses a service for data for listing, which I'm sure I can implement if I get a clue for this one.
My AddHandler Class
namespace Note\Handler;
use App\Service\CrmApiService;
use App\Service\CustomHydrator;
use Laminas\Diactoros\Response\RedirectResponse;
use Mezzio\Flash\FlashMessageMiddleware;
use Mezzio\Flash\FlashMessagesInterface;
use Note\Form\NoteForm;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Laminas\Diactoros\Response\HtmlResponse;
use Mezzio\Template\TemplateRendererInterface;
class AddHandler implements MiddlewareInterface
{
/** @var NoteForm $noteForm */
private $noteForm;
/** @var TemplateRendererInterface $renderer */
private $renderer;
/** @var string $usersJson */
private $usersJson;
/**
* AddHandler constructor.
* @param NoteForm $noteForm
* @param TemplateRendererInterface $renderer
*/
public function __construct(NoteForm $noteForm, TemplateRendererInterface $renderer)
{
$this->noteForm = $noteForm;
$this->renderer = $renderer;
}
/**
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$this->usersJson = (new CrmApiService())->getUsers();
$hydratedUsers = (new CustomHydrator())->getHydrated($this->usersJson);
$userArray = [];
foreach ($hydratedUsers as $user) {
$userArray[] = $user;
}
$userSelectValueOptions = [];
foreach ($userArray as $key => $val) {
$userSelectValueOptions[$val["personReference"]] = $val["givenName"] . " " . $val["additionalName"] . " " . $val["familyName"];
}
if ($request->getMethod() === "POST") {
$this->noteForm->setData(
$request->withoutAttribute("saveNote")->withoutAttribute("referrerId")->getParsedBody()
);
// NB: assignedUserID received by form submission is assigned a dummy User Name and is then
// appended at the end of formSelect("assignedUserID") for noteForm validation in below code block
$userSelectValueOptions[$this->noteForm->get("assignedUserID")->getValue()] = "Testing User";
$userSelect = $this->noteForm->get("assignedUserID");
$userSelect->setValueOptions($userSelectValueOptions);
//todo: remove the above code block before production
$referrerId = $request->getAttribute("referrerId");
$parent = $request->getAttribute("parent");
$parentID = $request->getAttribute("parentID");
if ($this->noteForm->isValid()) {
(new CrmApiService())->createNote($this->noteForm->getData());
$successMessage = "Note successfully added.";
$response = $handler->handle($request);
/** @var FlashMessagesInterface $flashMessages */
$flashMessages = $request->getAttribute(FlashMessageMiddleware::FLASH_ATTRIBUTE);
if ($response->getStatusCode() !== 302) {
$flashMessages->flash("success", $successMessage);
return new RedirectResponse(
(substr(
$referrerId,
0,
3
) == "brk" ? "/broker/" : "/enquiry/") . $referrerId . "/" . $parent . "/" . $parentID
);
}
return $response;
}
}
$referrerId = $request->getAttribute("referrerId");
$parentID = $request->getAttribute("parentID");
$parent = $request->getAttribute("parent");
$userSelect = $this->noteForm->get("assignedUserID");
$userSelect->setValueOptions($userSelectValueOptions);
$noteParent = $this->noteForm->get("parent");
$noteParent->setValue($parent);
$noteParentID = $this->noteForm->get("parentID");
$noteParentID->setValue($parentID);
return new HtmlResponse(
$this->renderer->render(
"note::edit",
[
"form" => $this->noteForm,
"parent" => $parent,
"parentID" => $parentID,
"referrerId" => $referrerId
]
)
);
}
}
PHP UnitTest
declare(strict_types=1);
namespace NoteTests\Handler;
use Note\Handler\AddHandler;
use Mezzio\Template\TemplateRendererInterface;
use Note\Form\NoteForm;
use Note\Handler\EditHandler;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class NoteAddEditHandlerTest extends TestCase
{
use ProphecyTrait;
/** @var NoteForm */
private $noteForm;
/** @var TemplateRendererInterface */
private $renderer;
public function testRendersAddFormProperly()
{
$this->renderer
->render("note::edit", Argument::type("array"))
->willReturn(true);
$serverRequest = $this->createMock(ServerRequestInterface::class);
$requestHandler = $this->createMock(RequestHandlerInterface::class);
$mock = $this->getMockBuilder(AddHandler::class)
->onlyMethods(["process"])
->setConstructorArgs([$this->noteForm, $this->renderer->reveal()])
->getMock();
$mock->expects($this->once())
->method("process")
->with($serverRequest, $requestHandler);
$mock->process($serverRequest, $requestHandler);
}
/**
*
*/
protected function setUp(): void
{
$this->noteForm = new NoteForm();
$this->renderer = $this->prophesize(TemplateRendererInterface::class);
}
}
Edit (Desired Result)
The AddHandler->process()
method renders a page and this is what I'd like to see that the UnitTest also test against a response but I'm not sure how to test that. I think there should be some return value at the end of this code block with will()
$mock->expects($this->once())
->method("process")
->with($serverRequest, $requestHandler);
Here's my solution. I've mocked ResponseInterface::class
as $this->responseInterface
and make the process
method to return this.
public function testRendersEditFormProperly()
{
$this->renderer
->render("note::edit", Argument::type("array"))
->willReturn(true);
$mock = $this->getMockBuilder(EditHandler::class)
->onlyMethods(["process"])
->setConstructorArgs([$this->noteForm, $this->renderer->reveal()])
->getMock();
$mock->method("process")
->with($this->serverRequest, $this->requestHandler)
->willReturn($this->responseInterface);
$response = $mock->process($this->serverRequest, $this->requestHandler);
$this->assertSame($response, $this->responseInterface);
}