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

时间:2013-09-20 09:57:29

标签: php unit-testing mocking phpunit

目前我正在测试一个基于值对象生成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;
    }
}

我想测试两件事:

  1. 给定某个RequestValues参数,生成的XML必须类似于$string
  2. 给定某个XML响应值(将模拟HTTP客户端),ResponseValues必须等于$object
  3. 我现在正在为#1编写测试,但我认为我只能通过回调来实现这一目标。但是,当测试失败时,回调并没有给我提供非常有用的信息。只有这条消息:

      

    声明指定的回调接受DOMDocument Object()失败。

    测试如下:

    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”,这极大地简化了调试。

    PS。 GitHub上提供了这个类的完整代码。当然,以上示例是简化版本。这是实际的课程:https://github.com/juriansluiman/SlmIdealPayment/blob/master/src/SlmIdealPayment/Client/StandardClient.php#L58

    PPS。如果必须更改代码才能对其进行测试,那不是问题。我只想保持公共API不变(即调用ResponseValues send(RequestValues $request)

1 个答案:

答案 0 :(得分:0)

你的班级MyClient表明它是一个门面。要在UnitTests的上下文中测试外观,通常首先需要测试作为该外观的协作者的所有单元。

正如您通常嘲笑协作者 - 而不是正在测试的单元 - 您的测试设置看起来不对,因为您正在嘲笑正在测试的MyClient单元但不是它的合作者。

例如:

您想测试MyClient::parseResponse()方法是否返回预期的ResponseValues对象。因此,你会模仿Response,因为在这种情况下它是一个合作者。

您可能还想模拟其他协作者(ResponseValues)但是您不能,因为它是无法注入的隐藏依赖项(您可以通过将工厂注入MyClient来解决此问题可以控制这种ResponseValues)的创建。

对于测试MyClient::parseResponse()这么多:你会通过Response模拟注入fixture XML,并在返回值上运行你的断言。

对于测试请求是否包含有效XML(testRequestContainsValidXml())的情况,我认为不应该以这种复杂的形式完成。听起来你在这里有一个HTTP客户端,你应该只测试它是否有效,并且不关心它是否也适用于XML,因为如果它工作,它也适用于XML。您测试有效XML的原因不是因为您要测试客户端。因此,请将该测试保留在客户端单元测试之外。

字符串的断言btw。是:

$this->assertSame($expected, $actual);

Phpunit会告诉你字符串之间的差异。其他一些程序员/测试人员在比较XML时也应用了一些XML规范化,以便差异更具可读性。如果您的XML没有问题可以处理无关紧要的空白,您可能会发现类似PHP XML how to output nice format的Q& A和相关的问题。

我希望我可以稍微指出你面临的问题,并扩大你对这个问题的看法。我认为你最关心的是批判性地质疑你当前的模拟用法,因为我给出的例子我已经完全减少了提供合作者的嘲弄,而不是做断言。 Asswertions仅在被测单元上完成,此处为示例MyClient