首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何为自定义服务使用UnitTest ()实现getMockBuilder?

如何为自定义服务使用UnitTest ()实现getMockBuilder?
EN

Stack Overflow用户
提问于 2021-06-26 08:14:37
回答 2查看 970关注 0票数 0

我正试图用Mezzio (Zend )为我的AddHandler::class编写一个PHP,但我不确定我做得对还是错。虽然测试通过了,但我并不认为这是正确的方法。基本的要求是模拟服务(new CrmApiService())->getUsers()(new CustomHydrator())->getHydrated($this->usersJson)的输出,这些输出可以保存在文本文件中。我有另一个ViewHandler::class,它也使用一个数据服务来列出数据,我相信如果我得到了这方面的线索,我可以实现它。

我的AddHandler类

代码语言:javascript
复制
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
                ]
            )
        );
    }
}

UnitTest

代码语言:javascript
复制
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);
    }

}

编辑(期望结果)

AddHandler->process()方法呈现一个页面,这就是我希望看到的UnitTest也会针对响应进行测试,但我不知道如何测试它。我认为在will()代码块的末尾应该有一些返回值。

代码语言:javascript
复制
$mock->expects($this->once())
            ->method("process")
            ->with($serverRequest, $requestHandler);
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-06-28 18:41:48

这是我的解决办法。我已经将ResponseInterface::class模拟为$this->responseInterface,并使process方法返回此方法。

代码语言:javascript
复制
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);
    }
票数 0
EN

Stack Overflow用户

发布于 2021-06-26 09:52:04

虽然通过了测试,但我并不真的相信这就是实现它的方法。

如果您编写了该测试,这是您的判断,我建议您暂时重写测试(例如,在另一种测试方法中),在测试中测试您的期望,以验证它们是否得到了解决。

否则,测试似乎对您没有好处,因为您不了解它测试的目的,因此是多余的代码和浪费(在敏捷的意义上),您可以干净地删除它,而不是让它潜伏在那里。

谁需要一个在测试中不清楚的测试?特别是在单元测试中,测试失败的原因应该只有一个。不清楚的测试是不可能的。

它是否已经清理时间,然后回到绘图板?也许吧。我建议先做一些渐进改进和一些沙箱。类似于添加一个大大简化的测试用例方法来验证您自己对测试套件框架和(两个?)的期望。模仿正在使用的图书馆/系统。

这也将帮助您开始使用该框架,并获得更深层次的理解--这通常会立即得到回报。

我有另一个ViewHandler::class,它也使用一个用于列表的数据服务,我相信如果我得到了这个方面的线索,我可以实现这个服务。

你的代码你的测试。只有你可以说你的测试是否完全满足了你的要求。

如果你允许我发表个人意见,我不喜欢在考试中嘲弄别人。即使对于代码模拟技术上的工作,它很快就变得很麻烦,并且有这样的趋势:测试只是测试为测试而编写的模拟,所以完全是不必要的工作。

相反,我尝试直接测试代码,如果某个抽象需要预先设置大量的设置,则为其创建一个工厂,然后该工厂也可以用于测试,从而将开销降到最低。

然后,工厂的一些专门化可以用于测试,自动注入测试配置(例如,如果必须设置空白的其他系统,测试不应该到达),则以模拟的形式注入,然后让它通过。但这只是典范。

在您喜欢测试system($request, $response)->assert(diverse on $response afterwards)的系统中,system是您编写的具体类的* (您的实现),您可能希望有一个*测试器,以便您的测试过程在system提供的和*实现的所有接口上保持清晰,并且您不需要只为测试任何* (例如HandlerTester )设置*的所有system的内部组件。

此外,如果处理程序需要更高级别的抽象实现,则检查Mezzio本身是否没有提供测试器。一个好的库通常附带好的测试实用程序(即使在这种情况下,您可以随时使用它)。

测试应该在开发之前进行,这对库来说是如此的正确,所以实际上我个人认为这些东西已经在0.0.1中出现了。但这可能会有所不同。

也为您的测试启用代码覆盖率,这样您就可以更容易地检查测试是否按照预期的方式运行,并将所有协作者都置于测试和覆盖之下。这可以帮助人们更好地理解一个测试的功能,并且可能已经弄清楚了它是否有用。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68140475

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档