Php 如何测试内部生成XML的类

Php 如何测试内部生成XML的类,php,unit-testing,mocking,phpunit,Php,Unit Testing,Mocking,Phpunit,目前,我正在测试一个类,该类基于值对象生成XML,通过HTTP发送XML,并将XML响应解析回第二个值对象。在本例中,我想测试生成的XML和基于给定XML的解析值对象 该类看起来像: class MyClient { public function send(RequestValues $request) { $document = $this->generateMessage($request); $response = $this->

目前,我正在测试一个类,该类基于值对象生成XML,通过HTTP发送XML,并将XML响应解析回第二个值对象。在本例中,我想测试生成的XML和基于给定XML的解析值对象

该类看起来像:

class MyClient
{
    public function send(RequestValues $request)
    {
        $document = $this->generateMessage($request);
        $response = $this->request($document);

        return $this->parseResponse($response);
    }

    protected function generateMessage(RequestValues $request)
    {
        $document = new DomDocument;
        // Do stuff with $request

        return $document;
    }

    public function request(DomDocument $document)
    {
        $client = $this->getHttpClient();
        $client->setRawBody($document->saveXml());
        // Configure client

        return $client->send();
    }

    public function parseResponse(Response $response)
    {
        $parameters = new ResponseValues;

        $document = new DomDocument;
        $document->loadXml($response->getBody());

        // Fill in $parameters
        return $parameters;
    }
}
我想测试两件事:

  • 给定某个
    RequestValues
    参数,生成的XML必须看起来像
    $string
  • 给定某个XML响应值(HTTP客户端将被模拟),响应值必须等于
    $object
  • 我现在正在为#1编写一个测试,但我认为我只能通过回调来实现这一点。然而,当测试失败时,回调并没有给我提供非常有用的信息。仅此消息:

    断言指定回调接受DOMDocument对象()失败

    测试如下所示:

    public function testRequestContainsValidXml()
    {
        $client  = $this->getMock('MyClient', array('request'));
    
        $message = '';
        $client->expects($this->once())
               ->method('request')
               ->with($this->callback(function($object) use ($message) {
                    return
                        ($object instanceof DomDocument)
                     && ($object->saveXml() === $message);
                }));
    
        $request = new DirectoryRequest;
        $client->send($request);
    }
    
    问题是:如何改进测试,以便可以进行正常的字符串比较?我想让phpunit说“字符串X不等于Y”,这大大简化了调试

    这个类的完整代码可以在GitHub上找到。当然,上面的例子是一个简化的版本。下面是实际的课程:


    PPS。如果为了测试代码而必须修改代码,这不是问题。我只想保持公共API不变(即调用
    ResponseValues send(RequestValues$request)

    您的类
    MyClient
    表明它是一个facade。要在UnitTests上下文中测试facade,通常首先需要测试作为该facade的协作者的所有单元

    由于您通常模拟的是协作者,而不是被测试的单元,因此您的测试设置看起来是错误的,因为您模拟的是被测试的
    MyClient
    单元,而不是它的协作者

    例如:

    您想测试
    MyClient::parseResponse()
    方法是否返回预期的
    ResponseValues
    对象。因此,在这种情况下,您将模拟
    响应
    ,因为它是协作者

    您可能还想模仿其他合作者(
    responsevalue
    ),但是您不能,因为它是一个无法注入的隐藏依赖项(您可以通过向
    MyClient
    注入一个工厂来解决这个问题,该工厂可以控制此类
    responsevalue
    的创建)

    测试
    MyClient::parseResponse()
    :您可以通过
    Response
    mock注入fixture XML,并在返回值上运行断言

    用于测试请求是否包含有效XML的案例(
    testRequestContainsValidXml()
    ),我也不认为它应该以如此复杂的形式进行。听起来你这里有一个HTTP客户机,你应该只测试它是否工作,而不关心它是否也能与XML一起工作,因为如果它工作,它也能与XML一起工作。你测试有效XML的原因不是因为你想测试客户机。因此,请将该测试排除在cl之外客户端单元测试

    顺便说一句,字符串stuff的断言是:

    $this->assertSame($expected, $actual);
    
    Phpunit将向您展示字符串之间的一个很好的差异。其他一些程序员/测试人员在比较XML时也应用了一些XML规范化,以使差异更具可读性。如果您的XML在处理无关紧要的空白方面没有问题,您可能会发现类似Q&a和相关问题在这方面很有用


    我希望我能用这一点指出您面临的问题案例,并拓宽您对这个问题的看法。我认为您对当前模拟用法的质疑最为尖锐,因为我给出的示例将模拟完全简化为提供合作者,而不是进行断言。这里只对测试单元进行了假设示例性的
    MyClient

    到底是什么问题?是比较
    $object->saveXml()吗==$message
    失败了?@silkfire如果$message是正确的,它不会失败并通过。但是,无法看到预期字符串和实际字符串之间的差异。您无法通过单元测试以任何方式对此进行调试,例如,
    $this->assertEquals($string1,$string2)可以进行调试
    。与其说单元测试帮助我理解代码中的bug,不如说它是一个大黑匣子,如果你意外地遇到bug,它并没有真正的帮助。所以问题是(也用粗体表示)如何改进测试。我可以让它通过测试,但问题是当它不通过测试时,要从phpunit返回有意义的消息。可以使用PHP的diff类逐个字符查看差异;然后可以在单元测试中调用diff函数?例如,FineDiff非常好;测试ir工具,利用此处的live类: