visual unit test studio net ejemplo unit-testing junit apache-storm

unit testing - unit - Prueba de pernos de tormenta y caños



unit test project c# (4)

Esta es una pregunta general acerca de los pernos y picos de prueba de unidades en una topología de tormenta escrita en Java.

¿Cuál es la práctica recomendada y la guía para la unidad de prueba (JUnit?) Pernos y Caños ?

Por ejemplo, podría escribir una prueba de JUnit para un Bolt , pero sin entender completamente el marco (como el ciclo de vida de un Bolt ) y las implicaciones de serialización, cometer fácilmente el error de creación de variables miembro no serializables basadas en Constructor. En JUnit, esta prueba pasaría, pero en una topología, no funcionaría. Me imagino completamente que hay muchos puntos de prueba que uno debe considerar (como este ejemplo con Serialización y ciclo de vida).

Por lo tanto, ¿se recomienda que si usa pruebas unitarias basadas en JUnit, ejecute una topología de simulación pequeña ( LocalMode ?) Y pruebe el contrato implícito para el Bolt (o Spout ) bajo esa Topología? O bien, ¿está bien utilizar JUnit, pero la consecuencia es que tenemos que simular el ciclo de vida de un Bolt (crearlo, llamar a prepare() , burlarse de una Config , etc.) con cuidado? En este caso, ¿cuáles son algunos puntos de prueba generales para la clase bajo prueba (Bolt / Spout) a considerar?

¿Qué han hecho otros desarrolladores con respecto a la creación de pruebas unitarias adecuadas?

Noté que hay una API de prueba de topología (Ver: https://github.com/xumingming/storm-lib/blob/master/src/jvm/storm/TestingApiDemo.java ). ¿Es mejor usar algo de esa API, y poner de pie "Topologías de prueba" para cada Bolt & Spout individual (y verificar el contrato implícito que el Bolt tiene que proporcionar, por ejemplo, se trata de salidas declaradas)?

Gracias



Nuestro enfoque es usar la inyección de constructor de una fábrica serializable en el pico / perno. El pico / perno luego consulta a la fábrica en su método de apertura / preparación. La única responsabilidad de la fábrica es encapsular la obtención de las dependencias de vertedor / perno de manera serializable. Este diseño permite que nuestras pruebas unitarias inyecten fábricas falsas / de prueba / falsas que, cuando sean consultadas, devuelvan los servicios falsos. De esta forma, podemos probar por un estrecho margen los picos / pernos usando simulacros, por ejemplo, Mockito.

A continuación se muestra un ejemplo genérico de un perno y una prueba para ello. He omitido la implementación de fábrica UserNotificationFactory porque depende de su aplicación. Puede utilizar los localizadores de servicios para obtener los servicios, la configuración serializada, la configuración accesible para HDFS o cualquier forma de obtener los servicios correctos, siempre que la fábrica pueda hacerlo después de un ciclo de servicio. Debe cubrir la serialización de esa clase.

Tornillo

public class NotifyUserBolt extends BaseBasicBolt { public static final String NAME = "NotifyUser"; private static final String USER_ID_FIELD_NAME = "userId"; private final UserNotifierFactory factory; transient private UserNotifier notifier; public NotifyUserBolt(UserNotifierFactory factory) { checkNotNull(factory); this.factory = factory; } @Override public void prepare(Map stormConf, TopologyContext context) { notifier = factory.createUserNotifier(); } @Override public void execute(Tuple input, BasicOutputCollector collector) { // This check ensures that the time-dependency imposed by Storm has been observed checkState(notifier != null, "Unable to execute because user notifier is unavailable. Was this bolt successfully prepared?"); long userId = input.getLongByField(PreviousBolt.USER_ID_FIELD_NAME); notifier.notifyUser(userId); collector.emit(new Values(userId)); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields(USER_ID_FIELD_NAME)); } }

Prueba

public class NotifyUserBoltTest { private NotifyUserBolt bolt; @Mock private TopologyContext topologyContext; @Mock private UserNotifier notifier; // This test implementation allows us to get the mock to the unit-under-test. private class TestFactory implements UserNotifierFactory { private final UserNotifier notifier; private TestFactory(UserNotifier notifier) { this.notifier = notifier; } @Override public UserNotifier createUserNotifier() { return notifier; } } @Before public void before() { MockitoAnnotations.initMocks(this); // The factory will return our mock `notifier` bolt = new NotifyUserBolt(new TestFactory(notifier)); // Now the bolt is holding on to our mock and is under our control! bolt.prepare(new Config(), topologyContext); } @Test public void testExecute() { long userId = 24; Tuple tuple = mock(Tuple.class); when(tuple.getLongByField(PreviousBolt.USER_ID_FIELD_NAME)).thenReturn(userId); BasicOutputCollector collector = mock(BasicOutputCollector.class); bolt.execute(tuple, collector); // Here we just verify a call on `notifier`, but we could have stubbed out behavior befor // the call to execute, too. verify(notifier).notifyUser(userId); verify(collector).emit(new Values(userId)); } }


Resulta bastante fácil burlarse de objetos de tormenta como OutputDeclarer, Tuple y OutputFieldsDeclarer. De ellos, solo OutputDeclarer ve algún efecto secundario, por lo que codifica la clase de prueba de OutputDeclarer para poder responder a cualquier tupla y anclas emitidas, por ejemplo. Su clase de prueba puede usar instancias de esas clases simuladas para configurar fácilmente una instancia de bolt / spout, invocarlo y validar los efectos secundarios esperados.


Un enfoque que hemos tomado es mover la mayor parte de la lógica de la aplicación de los pernos y los picos y dentro de los objetos que utilizamos para hacer el trabajo pesado al crear instancias y usarlos a través de interfaces mínimas. Luego hacemos pruebas unitarias en esos objetos y pruebas de integración, aunque esto deja un espacio.