ModulesInvoker { static StartResult start(final ExecutionContext context, final Iterable<SagaModule> modules) { Exception possibleStartException = null; List<Supplier<Optional<Exception>>> finishers = new ArrayList<>(); List<BiFunction<Object, Throwable, Optional<Exception>>> errorHandlers = new ArrayList<>(); try { for (final SagaModule module : modules) { finishers.add(createFinisher(module, context)); errorHandlers.add(createErrorHandler(module, context)); module.onStart(context); } } catch (Exception ex) { possibleStartException = ex; } return new StartResult(possibleStartException, new ModulesInvoker(finishers, errorHandlers)); } private  ModulesInvoker(
            final List<Supplier<Optional<Exception>>> finishers,
            final List<BiFunction<Object, Throwable, Optional<Exception>>> errorHandlers); Collection<Exception> error(final Object message, final Throwable error);  }
@Test public void start_listOfModules_callsStartOnAllModules() { CurrentExecutionContext context = mock(CurrentExecutionContext.class); ModulesInvoker.start(context, modules); verify(modules.get(0)).onStart(context); verify(modules.get(1)).onStart(context); verify(modules.get(2)).onStart(context); }
@Test public void start_startErrorOnSecondModule_doNotStartOrFinishedThird() { CurrentExecutionContext context = mock(CurrentExecutionContext.class); NullPointerException ex = new NullPointerException(); doThrow(ex).when(modules.get(1)).onStart(context); try { ModulesInvoker.start(context, modules); } catch (Exception e) { } verify(modules.get(2), never()).onStart(context); verify(modules.get(2), never()).onFinished(context); }
@Test public void start_exceptionOnSecond_doNotCallErrorOnThird() { CurrentExecutionContext context = mock(CurrentExecutionContext.class); Object message = new Object(); NullPointerException ex = new NullPointerException(); doThrow(ex).when(modules.get(1)).onStart(context); try { ModulesInvoker.start(context, modules); } catch (Exception e) { } verify(modules.get(2), never()).onError(context, message, ex); }