2013-05-14 12 views
45

Jest to ogólne pytanie dotyczące śrub do testowania jednostek i spustów w topologii burzowej napisanej w języku Java.Testowanie śrub burzowych i wylewek

Jakie jest zalecane praktyki i wytyczne dla testów jednostkowych (JUnit-?) Śruby i Wylewki?

Na przykład, mogę napisać test JUnit do Bolt, ale bez pełnego zrozumienia ramy (jak w cyklu życia Bolt) oraz implikacje serializacji, łatwo popełnić błąd tworzenia Konstruktor opartej non-serializable zmienne składowe. W JUnit test ten przejdzie, ale w topologii nie zadziała. W pełni wyobrażam sobie, że istnieje wiele punktów testowych, które należy wziąć pod uwagę (np. Ten przykład z cyklem życia serializacji &).

Dlatego zaleca, że ​​jeśli używasz testów jednostkowych JUnit oparte prowadzisz małą mock topologii (LocalMode?) I test dorozumianą umowę na Bolt (lub Spout) pod tym topologii? Czy może używać JUnit, ale implikuje to, że musimy symulować cykl życia Bolta (tworzenie go, wywoływanie prepare(), kpiny z Config itd.) Uważnie? W takim przypadku, jakie są ogólne punkty testowe dla badanej klasy (Bolt/Spout) do rozważenia?

Co zrobili inni deweloperzy, jeśli chodzi o tworzenie odpowiednich testów jednostkowych?

Zauważyłem, że istnieje API do testowania topologii (patrz: https://github.com/xumingming/storm-lib/blob/master/src/jvm/storm/TestingApiDemo.java). Czy lepiej używać niektórych z tych API i wstawiać "Topologie testowe" dla każdego pojedynczego użytkownika (i sprawdzać domyślną umowę, którą musi zapewnić Śruba, np. - Zadeklarowane wyjścia)?

Dzięki

+0

Czy kiedykolwiek zdecydować się na podejściu? –

+0

Cóż, czytam odpowiedzi poniżej. Wygląda na to, że istnieją pewne ogólne wytyczne, ale nic nie jest przesądzone. Zamierzam pozostawić to pytanie otwarte jeszcze przez jakiś czas, aby przekonać się, czy ktoś ma jakieś myśli, a potem je zamknąć. Podoba mi się oba podejścia do korzystania z Testing API (TestingApiDemo.java), a także twoja odpowiedź na kpiny z zależności @ChrisGerken. – Jack

Odpowiedz

8

Nasze podejście polega na wykorzystaniu wstrzyknięcia konstruktora z serializowalnej fabryki do dziobka/śruby. Dziobek/śruba następnie konsultuje się z fabryką w swojej otwartej/przygotowującej metodzie.Odpowiedzialnością fabryki jest hermetyzacja w celu uzyskania zależności między wylewką a śrubą w sposób umożliwiający jej szeregowanie. Ten projekt umożliwia naszym jednostkom testowym wstrzykiwanie fałszywych/testowych/fałszywych fabryk, które po konsultacji zwracają fałszywe usługi. W ten sposób możemy wąsko testować dziobek/śruby za pomocą mocks np. Mockito.

Poniżej znajduje się ogólny przykład śruby i test na nią. Pominąłem wdrożenie fabryki UserNotificationFactory, ponieważ zależy to od twojej aplikacji. Możesz użyć lokalizatorów usług w celu uzyskania usług, konfiguracji szeregowej, konfiguracji dostępnej dla HDFS lub w jakikolwiek inny sposób, aby uzyskać prawidłowe usługi, o ile fabryka może to zrobić po zakończeniu cyklu. Powinieneś opisać serializację tej klasy.

Bolt

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)); 
    } 
} 

test

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)); 
    } 
} 
+0

Wiem, że to stary post, więc wybacz mi, jeśli przeniosłeś się z tego projektu :) Zastanawiam się, czy test do weryfikacji (powiadamiający) .notifyUser (userId); minęło. Zauważyłem, że serializacja i deserializacja, którą Storm wykonuje w fabryce, powoduje utworzenie nowego fałszywego powiadamiającego. Zatem oryginalny fałszywy powiadamiający nie otrzymuje żadnych interakcji. Czy tak było w twoim przypadku? – ilana917

+0

@ ilana917 Burza i kpiny nie powinny wchodzić w interakcję. Celem tego wzoru jest napisanie kodu w sposób, który pozwala testować kod oddzielnie od środowiska wykonawczego Storm. W teście nie powinna wystąpić żadna serializacja. Pomiędzy wierszami 'new NotifyUserBolt' i' bolt.prepare' nie ma serializacji w teście. W środowisku wykonawczym Storm Storm dokona serializacji obiektu Bolt. –

+0

Dzięki @ carl-g, w tym kierunku idę. Znajduję się w środku dużego reaktora i początkowo planowałem przeprowadzenie burzy do końca jako pewnego rodzaju test integracyjny wykorzystujący makiety do połączeń zewnętrznych, ale testowanie jednostkowe naszej logiki ma więcej sensu. – ilana917

11

Jedno podejście podjęliśmy jest przeniesienie większości logiki aplikacji z śrub i otworów dyszy i do przedmiotów, które używamy zrobić dźwiganie ciężarów przez instancji i korzystania z nich za pośrednictwem minimalnych interfejsów. Następnie przeprowadzamy testy jednostkowe na tych obiektach i testach integracyjnych, chociaż to pozostawia lukę.

+2

, podczas gdy to, co mówisz, jest prawdą, i dobrym pomysłem, nie interesuje się PO w znajdowaniu rzeczy takich jak problemy z serializacją przed wykonaniem wdrożenia produkcyjnego. –

+0

Dziękuję za odpowiedź! Ciekawi mnie, co rozumiesz przez "zostawić przerwę"? – taylorcressy

+0

luka jest na styku burzy i naszych obiektów. ta część nie jest dokładnie testowana, ponieważ pojawia się tylko w testach integracyjnych, co jest kosztowne, aby uczynić ją wyczerpującą, więc istnieje pewien kod łączący, który nie jest dobrze zakodowany. –

1

Okazuje się, że dość łatwo udawać obiekty burzowe, takie jak OutputDeclarer, Tuple i OutputFieldsDeclarer. Spośród nich tylko OutputDeclarer kiedykolwiek widzi jakieś efekty uboczne, więc zakodować klasę próbną OutputDeclarer, aby móc odpowiedzieć na przykład na emitowane krotki i kotwice. Twoja klasa testowa może następnie użyć instancji tych fałszywych klas, aby łatwo skonfigurować instancję typu bolt/spout, wywołać ją i zweryfikować oczekiwane efekty uboczne.

14

Ponieważ obiekty testowania urządzenia w wersji 0.8.1 Storm zostały wystawione przez Java:

Na przykład, jak używać tego API rzucić okiem tutaj:

+6

Rzeczywiście dobre API, chociaż trochę więcej dokumentacji pomogłoby dużo w zrozumieniu tego. –

+0

Oto blog, który może być przydatny: http://www.pixelmachine.org/2011/12/17/Testing-Storm-Topologies.html Wyjaśnia interfejs API clojure do testowania burzy, ale w zasadzie to samo jako Java API. – asmaier