Pregunta

Al escribir código en Java, es muy útil abarcar composición y inyección de dependencia para hacer posible y fácil realizar pruebas de unidad puras simulando objetos colaboradores.

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

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

Digamos que tengo esta función estúpida:

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

Cuando la unidad prueba mymod , solo quiero probar que details_db y stats_db se invocan con los parámetros correctos y que los valores de retorno se utilizan correctamente. La capacidad de details_db y stats_db para generar el valor correcto se prueba en otros lugares.

Para resolver el problema, 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 pruébelo de esta manera (básicamente, aplastando 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 cargar con esa fea lista de módulos.

He probado un par de bibliotecas simuladas ( erlymock y ( este otro ) pero No estaba satisfecho.

¿Cómo pruebas unitarias de su código de erlang?

¡Gracias!

¿Fue útil?

Solución

Hay dos cosas a considerar aquí ...

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

  • módulos funcionales puros (también conocidos como módulos sin efectos secundarios)
  • módulos con efectos secundarios

(Debería leer eso y asegurarse de entender la diferencia, el efecto secundario más típico, y el que está en su código de muestra, es escribir en la base de datos).

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

La clave es que aproximadamente el 90% de su código debe estar en módulos puramente funcionales: usted reduce drásticamente su problema. (Podría pensar que esto no es "resolver" su problema, simplemente "reducirlo", y en su mayoría tendría razón ...)

Una vez que haya logrado esta separación, la mejor manera de probar en unidad los módulos con efectos secundarios es usar marco de prueba estándar .

La forma en que lo hacemos no es utilizar objetos simulados, sino cargar la base de datos en las funciones init_per_suite o init_per_test y luego ejecutar los módulos por sí mismos ...

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

Otros consejos

Segundo lo que dice Guthrie. Te sorprenderá la cantidad de lógica que puedes extraer en funciones puras.

Una de las cosas que he estado vinculando últimamente con los nuevos módulos parametrizados es el uso de módulos paramterizados para la inyección de dependencia. Evita el problema con listas de parámetros y diccionarios de procesos. Si puedes usar las versiones recientes de erlang, eso podría ser un buen ajuste también.

Gordon tiene razón en que el objetivo principal es probar las pequeñas funciones sin 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 las dependencias parametrizadas. Utilizar un registro, el diccionario de procesos, módulos parametrizados. El código será menos feo.

Costuras

No se centre en módulos variables como costuras de dependencia, deje que los procesos sean las costuras. Codificar el nombre de un proceso registrado es una oportunidad perdida de inyectar una dependencia.

Solo estoy respondiendo la pregunta que se me hizo directamente y no estoy tratando de juzgar si el autor debería estar haciendo esto en absoluto.

Utilizando meck puede escribir una prueba de unidad para su 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 cadenas solo para enfatizar que no son algo que se pasa realmente, sino un valor falso. Gracias al resaltado de sintaxis, son fáciles de detectar en el código de prueba.

Para ser honesto, soy un antiguo desarrollador de Java profundamente enamorado de Mockito recientemente cambié a Erlang, y ahora contribuyendo al mencionado proyecto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top