我正在为PHP Propel项目设置一个测试套件使用Phactory和PHPUnit。 我目前正在尝试对一个发出外部请求的函数,我想在模拟中存根对该请求的响应。
这是我尝试测试的类的片段:
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
在我刚刚创建的$endpoint
终结点对象上?
还是我做错了;有没有更好的方法来让我进行单元测试我的函数依赖于外部 Web 请求?
我确实阅读了有关"Stubbing and Mocking Web Services"的 PHPUnit 文档,但他们这样做的示例代码有 40 行长,不包括必须定义自己的 wsdl。 我很难相信这是我处理这个问题最方便的方式,除非SO的好人有强烈的感觉。
非常感谢任何帮助,我整天都在挂断这个。 谢谢!!
从测试的角度来看,您的代码有两个问题:
- URL 是硬编码的,因此您无法更改它以进行开发、测试或生产
- 终结点知道如何检索数据。从您的代码中,我无法说出端点的真正作用,但如果它不是低级的"只需获取数据"对象,则它不应该知道如何检索数据。
使用这样的代码,没有测试代码的好方法。您可以使用反射,更改代码等。这种方法的问题在于,您不测试实际对象,而是测试一些反射,这些反射进行了更改以与测试一起使用。
如果要编写"良好"的测试,终结点应如下所示:
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 的模拟,它根据你想要测试的内容返回一些默认响应。
下一个问题可能是:如何测试数据解析器?大多数情况下,你没有。如果它只是 php 标准函数的包装器,则不需要。你的DataParser应该是非常低级的,看起来像这样:
class DataParser {
public function fetchUrl($url) {
return file_get_contents($url);
}
}
如果你需要或想要测试它,你可以创建一个 Web 服务,它存在于你的测试中,充当"模拟",始终返回预配置的数据。然后,您可以调用此模拟 url 而不是真实 url 并评估返回。