2009-07-29 13 views
20

Al escribir código en Java, es muy útil abarcar composition y dependency injection para hacer posible y fácil hacer pruebas unitarias puras al burlar objetos colaboradores.¿Cómo hago la inyección de dependencia y me burlo en Erlang?

Me parece que hacer lo mismo en Erlang es menos sencillo y hace que el código sea más sucio.

Eso es probable que sea mi culpa, ya que soy bastante nuevo en Erlang y bastante adicto a JUnit, EasyMock e interfaces Java ...

Digamos que tengo esta estúpida función:

%% module mymod 
handle_announce(Announce) -> 
    AnnounceDetails = details_db:fetch_details(Announce), 
    AnnounceStats = stats_db:fetch_stats(Announce), 
    {AnnounceDetails, AnnounceStats}. 

Al probar la unidad mymod, solo deseo probar que details_db y stats_db se invocan con los parámetros correctos y que los valores de retorno se usan correctamente. La capacidad de details_db y stats_db para generar el valor correcto se ha probado en otros lugares.

Para resolver el problema que podría refactorizar mi código de esta manera:

%% module mymod 
handle_announce(Announce, [DetailsDb, StatsDb]) -> 
    AnnounceDetails = DetailsDb:fetch_details(Announce), 
    AnnounceStats = StatsDb:fetch_stats(Announce), 
    {AnnounceDetails, AnnounceStats}. 

Y probar de esta manera (básicamente apagando las llamadas directamente en el módulo de prueba):

%% module mymod_test 
handle_announce_test() -> 
    R = mymod:handle_announce({announce, a_value}, [?MODULE, ?MODULE, ?MODULE]), 
    ?assertEqual({details,stats}, R). 

fetch_details({announce, a_value}) -> 
    details. 

fetch_stats({announce, a_value}) -> 
    stats. 

Funciona, pero el código de la aplicación se ensucia y siempre tengo que llevar esa fea lista de módulos.

Yo he probado un par de bibliotecas simuladas (erlymock y (this other one) pero no estaba satisfecho.

¿Cómo se prueba su unidad de código Erlang?

Gracias!

Respuesta

22

Hay hay dos cosas a considerar aquí ...

Necesita separar todo su código en 2 tipos diferentes de módulos:

  • módulos puros funcionales (también conocido como de efectos secundarios módulos libres)
  • módulos con efectos secundarios

(Usted debe leer para arriba en eso y estar seguro de que usted entiende la diferencia - El lado más típico -efecto - y el que está en su código de muestra - está escribiendo en la base de datos).

Los módulos que son completamente funcionales se vuelven triviales para probar. Cada función exportada (por definición) siempre devuelve los mismos valores cuando se colocan los mismos valores. Puede usar el marco EUnit/Assert que escribieron Richard Carlsson y Mickael Remond. Bish-bash-bosh, es un buen trabajo ...

Lo importante es que aproximadamente el 90% de su código debe estar en módulos funcionales puros: reduce drásticamente el problema. (Podrías pensar que esto no es 'resolver' tu problema, simplemente 'reducirlo' - y estarías en su mayoría en lo cierto ...)

Una vez que haya logrado esta separación, la mejor manera de probar los módulos con efectos secundarios es usar el standard test framework.

La forma de hacer esto es no usar simulaciones objetos - pero para cargar la base de datos en el init_per_suite o init_per_test funciones y luego ejecutar los propios módulos ...

La mejor manera es pasar directamente hacia El sistema prueba tan pronto como sea posible para esto, ya que las pruebas unitarias son difíciles de mantener, por lo que suficientes pruebas unitarias para llevarlo a un examen de sistema de ida y vuelta y nada más (incluso mejor eliminar las pruebas de unidad de db tan pronto como sea posible) .

+0

Gracias Gordon, muy bien explicado. Todavía estoy tratando de cambiar al paradigma funcional. De todos modos, en este proyecto que estoy escribiendo (un rastreador de torrents), todas las llamadas se originan en desde la capa web y terminan en la base de datos, por lo que la mayoría de los módulos tienen o dependen de los efectos colaterales. Voy a probar el marco de prueba estándar. –

+0

Good Erlang es tener muchas funciones pequeñas. Refactorice y divida su código en módulos de utilidad y se sorprenderá de lo poco que implica escribir en la base de datos. 15 - 25 líneas es una función larga en Erlang. Una función pura es aquella que toma un conjunto de parámetros, los calcula y simplemente devuelve un valor; debe tener muchos. –

+0

En realidad, no estoy de acuerdo en que deba pasar a las pruebas de integración lo antes posible. Con los módulos parametrizados y la burla provista por erlymock, la función dada sería trivial de probar. Puede probarlo dentro del propio módulo burlándose de los dos módulos db durante la prueba. Puede usar este módulo en otras pruebas haciendo que los módulos db sean parámetros de su módulo. Cualquiera que mire burlarse de erlang debería echar un segundo vistazo a erlymock, el principal problema es la falta de documentación, por lo que realmente tiene que leer la fuente para aceptarlo. –

4

Gordon tiene razón en que el objetivo principal es probar las pequeñas funciones libres de efectos secundarios.

Pero ... bueno, también es posible probar la integración, así que vamos a mostrar cómo se puede hacer eso.

inyección

Evitar listas para llevar a las dependencias parametrizados. Use un registro, el diccionario de proceso, módulos parametrizados. El código será menos feo.

costuras

no se centran en los módulos de variables como las costuras de dependencia, vamos procesos sean las costuras. La codificación forzada de un nombre de proceso registrado es una oportunidad perdida de inyectar una dependencia.

+2

No recomendaría usar el diccionario de procesos. Todo parece agradable y fácil, pero es ESTADO COMPARTIDO y eventualmente te morderá duro con bichos difíciles de encontrar. En Erlang es muy difícil conceptualizar y racionalizar el ciclo de vida de un proceso (aunque usted podría pensar que puede hacerlo) y un cambio en el ciclo de vida del proceso es donde su diccionario de proceso limpio sale mal. Lo sé por amarga experiencia :( –

+1

@Christian, el enlace a emock parece estar muerto. ¿Es esto lo mismo: https://github.com/noss/emock? –

4

Lo segundo que dice Guthrie. Te sorprenderá cuánto de tu lógica se puede extraer en funciones puras.

Una de las cosas que he estado atando últimamente con los nuevos módulos parametrizados es usar módulos paramterizados para la inyección de dependencia. Evita el problema con listas de parámetros y diccionarios de proceso. Si puedes usar las versiones recientes de erlang, también podría ser una buena opción.

3

Estoy respondiendo la pregunta directamente y no tratando de juzgar si el autor debería estar haciendo esto en absoluto.

Usando meck puede escribir una prueba unitaria para usted ejemplo de la siguiente manera:

handle_announce_test() -> 
    %% Given 
    meck:new([details_db, stats_db]), 
    meck:expect(details_db, fetch_details, ["Announce"], "AnnounceDetails"), 
    meck:expect(stats_db, fetch_stats, ["Announce"], "AnnounceStats"), 
    %% When 
    Result = handle_announce("Announce"), 
    %% Then 
    ?assertMatch({"AnnounceDetails", "AnnounceStats"}, Result), 
    %% Cleanup 
    meck:unload(). 

utilizo sólo para cuerdas énfasis que no son algo que se pasa en realidad, sino más bien un valor falso. Gracias a la sintaxis resaltada son fáciles de detectar en el código de prueba.

Para ser sincero, soy un antiguo desarrollador de Java profundamente enamorado de Mockito recientemente cambiado a Erlang, y ahora contribuyo al proyecto antes mencionado.

Cuestiones relacionadas