Estoy trabajando en la configuración de un conjunto de pruebas para un proyecto de PHP Propel usando Phactory, y PHPUnit. Actualmente estoy intentando probar la unidad en una función que hace una solicitud externa, y quiero resguardarme en una respuesta falsa para esa solicitud.¿Cómo puedo simular una solicitud web externa en PHPUnit?
He aquí un fragmento de la clase que estoy tratando de prueba:
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);
}
...
y aquí está la función de prueba que estoy tratando de escribir.
// 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);
}
puedo ver ahora, después probé mi código de prueba, que estoy creando un objeto de burla con getMock
, y luego no usarlo. Así que la función fetchUrl
en realidad se ejecuta, lo que no quiero. Pero todavía quiero poder usar el objeto Phactory creado endpoint
, ya que tiene todos los campos correctos poblados de mi definición de fábrica.
¿Hay alguna manera de que resida un método en un objeto existente? ¿Así que podría insertar fetch_url
en el objeto de punto final $endpoint
que acabo de crear?
¿O estoy haciendo todo mal? ¿Hay alguna manera mejor para que pruebe unitariamente mis funciones que dependen de solicitudes web externas?
He leído la documentación de PHPUnit con respecto a "Stubbing and Mocking Web Services", pero su código de muestra para hacerlo es de 40 líneas, sin incluir tener que definir tu propio wsdl. Me cuesta creer que esa es la forma más conveniente de manejar esto, a menos que las buenas personas de SO sientan lo contrario.
Agradezco muchísimo cualquier ayuda, he estado colgado de esto todo el día. ¡¡Gracias!!
Esto es lo que terminé haciendo, aunque no estoy muy contento con él. Una clase adicional para ajustar 'file_get_contents' _feels_ overkill para mí. Es un cambio de código que haría solo para probarlo, y eso me resulta desagradable. Vengo de ruby, que tenía la gema [webmock] (https://github.com/bblimke/webmock/), que hace exactamente lo que describiste en el último párrafo: "crea un servicio web que vive dentro de tus pruebas y actúa como un "simulacro", siempre devolviendo datos preconfigurados ". En lugar de codificar yo mismo, iré por la ruta del objeto simulado por el momento. Gracias por los pensamientos! – goggin13
No creo que sea sobrecargado. Piénselo de esta manera: va a utilizar este código en muchos lugares. En un año a partir de ahora, hay una forma mejor implementación para file_get_contents y debe cambiarlo en todas partes. O piense que quiere cambiar a Buzz (que recomendaría). En el código anterior, inyectas el objeto y tienes que cambiar una llamada a un método. Mayvbe está sobrecargado en su pequeño ejemplo, pero a medida que su aplicación aumenta de tamaño y es posible que desee reutilizar file_get_contents, puede apreciarlo. – Sgoettschkes