7

我正在使用PhactoryPHPUnit为 PHP Propel项目设置测试套件。我目前正在尝试对发出外部请求的函数进行单元测试,并且我想在该请求的模拟响应中存根。

这是我要测试的类的片段:

class Endpoint {
  ...
  public function parseThirdPartyResponse() {
    $response = $this->fetchUrl("www.example.com/api.xml");
    // do stuff and return 
    ...
  }

  public function fetchUrl($url) {
    return file_get_contents($url);
  }
  ...

这是我正在尝试编写的测试函数。

// my factory, defined in a seperate file
Phactory::define('endpoint', array('identifier'  => 'endpoint_$n');

// a test case in my endpoint_test file
public function testParseThirdPartyResponse() {
  $phEndpoint = Phactory::create('endpoint', $options);
  $endpoint = new EndpointQuery()::create()->findPK($phEndpoint->id);

  $stub = $this->getMock('Endpoint');
  $xml = "...<target>test_target</target>...";  // sample response from third party api

  $stub->expects($this->any())
       ->method('fetchUrl')
       ->will($this->returnValue($xml));

  $result = $endpoint->parseThirdPartyResponse();
  $this->assertEquals('test_target', $result);
}

我现在可以看到,在我尝试了我的测试代码之后,我正在创建一个模拟对象getMock,然后从不使用它。所以该函数fetchUrl 实际上执行了,这是我不想要的。但我仍然希望能够使用 Phactory 创建的endpoint对象,因为它具有从我的工厂定义中填充的所有正确字段。

有没有办法让我在现有对象上存根方法?所以我可以存根 刚刚创建fetch_url$endpointEndpoint 对象吗?

还是我做错了?有没有更好的方法来对依赖于外部 Web 请求的函数进行单元测试?

我确实阅读了有关“存根和模拟 Web 服务”的 PHPUnit 文档,但是他们这样做的示例代码有 40 行长,不包括必须定义自己的 wsdl。我很难相信这是我处理这个问题的最方便的方法,除非 SO 的好人强烈反对。

非常感谢任何帮助,我整天都在挂断电话。谢谢!!

4

1 回答 1

12

从测试的角度来看,您的代码有两个问题:

  1. url 是硬编码的,让您无法更改它以进行开发、测试或生产
  2. 端点知道如何检索数据。从您的代码中,我无法说出端点的真正作用,但如果它不是低级别的“Just get me Data”对象,它不应该知道如何检索数据。

使用这样的代码,没有很好的方法来测试您的代码。您可以使用反射、更改代码等。这种方法的问题是你没有测试你的实际对象,而是一些反射改变了测试。

如果你想编写“好”的测试,你的端点应该是这样的:

class Endpoint {

    private $dataParser;
    private $endpointUrl;

    public function __construct($dataParser, $endpointUrl) {
        $this->dataPartser = $dataParser;
        $this->endpointUrl = $endpointUrl;
    }

    public function parseThirdPartyResponse() {
        $response = $this->dataPartser->fetchUrl($this->endpointUrl);
        // ...
    }
}

现在您可以注入 DataParser 的 Mock,它会根据您要测试的内容返回一些默认响应。

下一个问题可能是:如何测试 DataParser?大多数情况下,你没有。如果它只是 php 标准函数的包装器,则不需要。您的 DataParser 应该非常低级,如下所示:

class DataParser {
    public function fetchUrl($url) {
        return file_get_contents($url);
    }
}

如果您需要或想要测试它,您可以创建一个 Web 服务,它存在于您的测试中并充当“模拟”,始终返回预配置的数据。然后,您可以调用这个模拟 url 而不是真实的 url 并评估返回。

于 2012-07-02T13:35:36.430 回答