2011-04-03 12 views
11

Supongamos que he decidido escribir una aplicación grande en C o en cualquier otro lenguaje de programación de procedimientos. Tiene funciones con call-dependencias que tener este aspecto:Inyección de dependencia para la programación de procedimiento

A 
| 
+-------------+ 
|    | 
B1   B2 
|    | 
+------+  +------+ 
|  |  |  | 
C11 C12 C21 C22 

Obviamente, la unidad de pruebas de las funciones de hojas, C11, C12, C21 y C22 es muy fácil: Configuración de las entradas, invocar las funciones, afirmar la salidas.

Pero, ¿cuál es la estrategia adecuada para permitir una buena prueba unitaria para B1, B2 y A?

¿Dependency Injection sugieren que B1 (y B2 también) se declaren como se siguen?

// Declare B1 with dependency injection for invoking C11 and C12. 
int B1(int input, int (*c11)(int), int(*c12)(int)); 

Pero esa estrategia no parece escalable si tengo muchas capas de llamadas. Imagínense lo que la declaración de A se vería así:

int A(int input, int (*b1)(int, int (*)(int), int(*)(int)), 
       int(*b2)(int, int (*)(int), int(*)(int)), 
       int (*c11)(int), 
       int (*c12)(int), 
       int (*c21)(int), 
       int (*c22)(int)); 

qué asco! Tiene que haber una mejor manera.

A veces, siento que DI y otros patrones similares que pretenden promover la modularidad y la facilidad de mantenimiento en realidad dificultan la claridad del código y complican lo que debería ser la codificación directa en abstracciones sin sentido e indirecciones intrincadas.

¿Cómo se tratan los grandes proyectos de software en C, como Perl y Ruby, con pruebas unitarias?

Respuesta

3

Si solo necesita la DI para probar la unidad, puede usar el enlazador para hacerlo.

Lo que quiero decir es que las funciones B1 & B2 están declaradas en un encabezado y utilizadas por la función A, por lo que la implementación de las funciones B es proporcionada por el enlazador. Solo necesita proporcionar un C-File diferente para las pruebas unitarias. Esto no debería ser un gran problema, ya que es probable que tenga su propio archivo MAKE para la prueba de la unidad de todos modos.

Si necesita resolución de dependencia dinámica en tiempo de ejecución, debe usar un patrón de fábrica (una función que devuelve el puntero a la función) para los punteros de funciones y extraerlos de la fábrica cuando sea necesario. La fábrica puede decidir, dependiendo del contexto global, qué función devolver.

+0

Esta es una idea interesante. Tendré que intertarlo. – kirakun

+0

También puede programar OO en C, no solo de procedimiento. Simplemente declare una estructura con miembros de datos y funciones tomando la estructura como primer argumento (pseudo este puntero). Implementación en archivo c y tú mismo has creado una pseudoclase. El polimorfismo y cosas así son posibles, simplemente complicadas;) – sanosdole

0

Me gusta esta pregunta. Se vuelve un poco complicado en los lenguajes de procedimiento ... pero creo que se puede tomar prestada una idea del mundo OO donde la gente a menudo usa la sobrecarga del constructor para manejar parte del trabajo DI. Entonces, por ejemplo, usaría el constructor predeterminado que configura todas las dependencias como de costumbre ... pero también tiene otro constructor que permite inyectar las dependencias.

Dado que usted es un procedimiento ... Creo que podría usar la sobrecarga de funciones para manejar esto por usted. Además, cuando realice la prueba, solo deberá simular B1 & B2 al llamar a A ... para simplificar su DI con ese fin. En otras palabras, si realmente sólo con DI para las pruebas unitarias, entonces no tiene que inyectar todo el árbol de dependcies sólo los primeros dependecies nivel ...

desde una que podría tener ...

int A(int input){ 
// create function point to b1 & b2 and call "return A(input, {pointer to b1},{pointer to b2})" 
} 

perdona mis pseudo código que ha pasado mucho tiempo desde que hice C.

2

puede poner las dependencias a un c-estructura que se convertirá en un parámetro de la llamada de función. en c esto sería similar al archivo api donde el primer parámetro es siempre el archivo-handle

+0

Hacemos algo similar en otros idiomas, tenemos un proveedor de servicios que tiene implementaciones de todo tipo de dependencias en él. Tiene la ventaja adicional de causar menos cambios para las firmas de funciones y facilitar las refactorizaciones. – Enno

3

A solo necesita llamar al B1 y B2. No necesita saber nada en el nivel C.

Puede inyectar diferentes versiones ficticias de las funciones B1 y B2 en A con el fin de probar A.

Esto aísla A de necesitar toda la estructura y significa que puede probar cada función de forma aislada.

0

Puede realizar pruebas unitarias correctas de B1, B2 y A sin DI. Al igual que las funciones de hoja, B1 y B2 tienen entradas y salidas válidas, y usted las prueba, lo mismo con A. Que B1 puede usar C11 y C12 internamente para ayudarlo a cumplir sus pruebas unitarias, no significa que tengan que ser inyectadas en casos donde no necesitas esa flexibilidad.

+0

Pero, ¿cómo se escala? En la especificación, 'A' cubrirá las funcionalidades de' B1' y 'B2', que también cubren las funcionalidades de' C11', 'C12',' C21' y 'C22'. Por lo tanto, todas las pruebas para las funciones de hoja 'C11', etc., tendrían que repetirse para todas las funciones de nodo que están sobre ellas. Ahora, imagina un conjunto de códigos profundamente estratificados. Es posible que las pruebas de escritura sean bastante laboriosas. – kirakun

+0

@Kirakun En casos prácticos, A no está haciendo lo mismo que B1 y B2, sino que está agregando algún tipo de funcionalidad adicional. Si tiene pruebas para B1 y B2, puede estar seguro de esa parte del código. Para A, solo prueba lo que necesitas para estar seguro de cómo une B1 y B2. – tddtrying

+0

@tddtrying La prueba de la unidad * de IMHO (en el escenario anterior) significa probar las funciones 'A * * sin * prueba (es decir, llamadas) en el nivel B, sin mencionar el nivel C. El nivel B podría ser discutible, pero imagine que 'C11' se conecta a una base de datos. No quiere una prueba unitaria para 'A' (o' B1') para llamar al 'C11' real entonces. En su lugar, debería llamarse una especie de simulacro 'C11'. – TobiMcNamobi

Cuestiones relacionadas