unit test php unit-testing codeigniter phpunit

test - ¿Cómo uso PHPUnit con CodeIgniter?



codeigniter unit test (2)

Intenté sin éxito utilizar PHPUnit con Codeigniter. Por ejemplo, si quería probar mis Modelos de CI, me encontré con el problema de cómo obtendré una instancia de ese modelo, ya que de alguna manera necesita todo el marco de CI para cargarlo. Considere cómo carga un modelo, por ejemplo:

$this->load->model("domain_model");

El problema es que si miras la súper clase para un método de carga, no la encontrarás. No es tan sencillo si está probando objetos simples antiguos de PHP donde puede burlarse de sus dependencias fácilmente y probar la funcionalidad.

Por lo tanto, me he conformado con la clase de pruebas de Unidad de CI .

my apps grab a bunch of data from the database then display it in some sort of table.

Si está probando sus controladores, básicamente está probando la lógica de negocios (si tiene) allí, así como la consulta SQL que "toma un montón de datos" de la base de datos. Esto ya es una prueba de integración.

La mejor forma es probar primero el modelo de CI para probar el acaparamiento de datos --- esto será útil si tienes una consulta muy complicada - y luego el controlador junto a la lógica de negocios que se aplica a los datos tomados por el modelo de CI. Es una buena práctica probar solo una cosa a la vez. Entonces, ¿qué probarás? La consulta o la lógica de negocios?

Supongo que primero quiere probar el acaparamiento de datos, los pasos generales son

  1. Obtenga algunos datos de prueba y configure su base de datos, tablas, etc.

  2. Tenga algún mecanismo para completar la base de datos con los datos de prueba, así como también eliminarla después de la prueba. La extensión de la base de datos de PHPUnit tiene una forma de hacerlo, aunque no sé si eso es compatible con el marco que publicaste. Haznos saber.

  3. Escribe tu prueba, pásala.

Su método de prueba podría verse así:

// At this point database has already been populated public function testGetSomethingFromDB() { $something_model = $this->load->model("domain_model"); $results = $something_model->getSomethings(); $this->assertEquals(array( "item1","item2"), $results); } // After test is run database is truncated.

En caso de que quiera usar la clase de prueba unitaria de CI, aquí hay un fragmento de código modificado de una prueba que escribí al usarlo:

class User extends CI_Controller { function __construct() { parent::__construct(false); $this->load->model("user_model"); $this->load->library("unit_test"); } public function testGetZone() { // POPULATE DATA FIRST $user1 = array( ''user_no'' => 11, ''first_name'' => ''First'', ''last_name'' => ''User'' ); $this->db->insert(''user'',$user1); // run method $all = $this->user_model->get_all_users(); // and test echo $this->unit->run(count($all),1); // DELETE $this->db->delete(''user'',array(''user_no'' => 11)); }

He leído y leído artículos sobre PHPUnit, SimpleTest y otros frameworks de Unit Testing. ¡Todos suenan tan bien! Finalmente conseguí PHPUnit trabajando con Codeigniter gracias a https://bitbucket.org/kenjis/my-ciunit/overview

Ahora mi pregunta es, ¿cómo la uso?

Cada tutorial que veo tiene un uso abstracto como assertEquals(2, 1+1) o:

public function testSpeakWithParams() { $hello = new SayHello(''Marco''); $this->assertEquals("Hello Marco!", $hello->speak()); }

Eso es genial si tuviera una función que produjera una cadena predecible. Por lo general, mis aplicaciones toman un montón de datos de la base de datos y luego los muestran en algún tipo de tabla. Entonces, ¿cómo pruebo los controladores de Codeigniter?

Me gustaría hacer un desarrollo basado en pruebas y he leído el tutorial en el sitio PHPUnits, pero una vez más el ejemplo parece muy abstracto. La mayoría de mis funciones codeigniter están mostrando datos.

¿Hay un libro o un gran tutorial con una aplicación práctica y ejemplos de pruebas de PHPUnit?


Parece que usted entiende la estructura / sintaxis básica de cómo escribir pruebas y pruebas unitarias CodeIgniter no debería ser diferente de probar código que no es CI, por lo que quiero centrarme en sus preocupaciones / problemas subyacentes ...

Tenía preguntas similares no hace mucho tiempo con PHPUnit. Como alguien sin entrenamiento formal, descubrí que entrar en la mentalidad de Prueba de Unidad parecía abstracto y antinatural al principio. Creo que la razón principal de esto, en mi caso, y probablemente también en el tuyo por la pregunta, es que no te has centrado en REALMENTE trabajar para separar las preocupaciones en tu código hasta ahora.

Las afirmaciones de prueba parecen abstractas porque la mayoría de sus métodos / funciones probablemente realicen varias tareas discretas diferentes. Una mentalidad de prueba exitosa requiere un cambio en cómo piensas sobre tu código. Debe dejar de definir el éxito en términos de "¿funciona?" En su lugar, debe preguntar: "¿Funciona? ¿Funcionará bien con otro código? ¿Está diseñado de manera que lo haga útil en otras aplicaciones y puedo verificar que funcione?"

Por ejemplo, a continuación se muestra un ejemplo simplificado de cómo probablemente ha escrito el código hasta este punto:

function parse_remote_page_txt($type = ''index'') { $remote_file = ConfigSingleton::$config_remote_site . "$type.php"; $local_file = ConfigSingleton::$config_save_path; if ($txt = file_get_contents($remote_file)) { if ($values_i_want_to_save = preg_match(''//'', $text)) { if (file_exists($local_file)) { $fh = fopen($local_file, ''w+''); fwrite($fh, $values_i_want_to_save); fclose($fh); return TRUE; } else { return FALSE; } } else { return FALSE; } }

Exactamente lo que está pasando aquí no es importante. Estoy tratando de ilustrar por qué este código es difícil de probar:

  • Está utilizando una clase de configuración singleton para generar valores. El éxito de su función depende de los valores del singleton, y ¿cómo puede probar que esta función funciona correctamente en completo aislamiento cuando no puede instanciar nuevos objetos de configuración con valores diferentes? Una mejor opción podría ser pasarle a su función un argumento de $config que consiste en un objeto o matriz de configuración cuyos valores puede controlar. Esto se denomina ampliamente " Inyección de Dependencia " y hay discusiones de esta técnica en todo el interwebs.

  • Observe las declaraciones IF anidadas. Las pruebas significan que está cubriendo cada línea ejecutable con algún tipo de prueba. Cuando anida las declaraciones IF, está creando nuevas ramas de código que requieren una nueva ruta de prueba.

  • Finalmente, ¿ves cómo esta función, aunque parece estar haciendo una cosa (analizar el contenido de un archivo remoto), en realidad está realizando varias tareas? Si celosamente separa sus preocupaciones, su código se vuelve infinitamente más comprobable. Una forma mucho más comprobable de hacer lo mismo sería ...

class RemoteParser() { protected $local_path; protected $remote_path; protected $config; /** * Class constructor -- forces injection of $config object * @param ConfigObj $config */ public function __construct(ConfigObj $config) { $this->config = $config; } /** * Setter for local_path property * @param string $filename */ public function set_local_path($filename) { $file = filter_var($filename); $this->local_path = $this->config->local_path . "/$file.html"; } /** * Setter for remote_path property * @param string $filename */ public function set_remote_path($filename) { $file = filter_var($filename); $this->remote_path = $this->config->remote_site . "/$file.html"; } /** * Retrieve the remote source * @return string Remote source text */ public function get_remote_path_src() { if ( ! $this->remote_path) { throw new Exception("you didn''t set the remote file yet!"); } if ( ! $this->local_path) { throw new Exception("you didn''t set the local file yet!"); } if ( ! $remote_src = file_get_contents($this->remote_path)) { throw new Exception("we had a problem getting the remote file!"); } return $remote_src; } /** * Parse a source string for the values we want * @param string $src * @return mixed Values array on success or bool(FALSE) on failure */ public function parse_remote_src($src='''') { $src = filter_validate($src); if (stristr($src, ''value_we_want_to_find'')) { return array(''val1'', ''val2''); } else { return FALSE; } } /** * Getter for remote file path property * @return string Remote path */ public function get_remote_path() { return $this->remote_path; } /** * Getter for local file path property * @return string Local path */ public function get_local_path() { return $this->local_path; } }

Como puede ver, cada uno de estos métodos de clase maneja una función particular de la clase que es fácilmente comprobable. ¿Funcionó la recuperación remota de archivos? ¿Encontramos los valores que estábamos tratando de analizar? Etc. De repente, esas afirmaciones abstractas parecen mucho más útiles.

En mi humilde opinión, cuanto más se adentra en las pruebas, más se da cuenta de que se trata más de un buen diseño de códigos y una arquitectura sensata que simplemente asegurarse de que todo funcione como se espera. Y aquí es donde realmente comienzan a brillar los beneficios de OOP. Puede probar el código de procedimiento muy bien, pero para un proyecto grande con pruebas de partes interdependientes tiene una forma de imponer un buen diseño. Sé que puede ser un cebo trol para algunas personas de procedimiento, pero bueno.

Mientras más pruebas, más te encontrarás escribiendo códigos y preguntándote: "¿Podré probar esto?" Y si no, probablemente cambiarás la estructura en ese momento.

Sin embargo, el código no necesita ser elemental para ser comprobable. Tropezar y burlarse le permite probar operaciones externas cuyo éxito o fracaso está completamente fuera de control. Puede crear fixtures para probar operaciones de bases de datos y casi cualquier otra cosa.

Cuanto más pruebo, más me doy cuenta de que si tengo problemas para probar algo es más probable porque tengo un problema de diseño subyacente. Si lo aclaro, generalmente resulta en todas las barras verdes en los resultados de mi prueba.

Finalmente, aquí hay un par de enlaces que realmente me ayudaron a comenzar a pensar de manera amigable para los exámenes. La primera es una lista irónica de lo que NO debes hacer si quieres escribir un código comprobable . De hecho, si navega por todo el sitio, encontrará muchas cosas útiles que lo ayudarán a establecer el camino hacia la cobertura del 100% del código. Otro artículo útil es esta discusión sobre la inyección de dependencia .

¡Buena suerte!