public FlowlessSystemPart<T> system(Consumer<T> systemReaction) { userPart.system(systemReaction); return new FlowlessSystemPart<>(flowlessStepCounter); } }
/** * Defines the type of user command objects that this step accepts. Commands of this * type can cause a system reaction. * * <p> * Given that the step's condition is true, and the actor is right, the system * reacts to objects that are instances of the specified class or instances of * any direct or indirect subclass of the specified class. * * @param eventClass * the class of commands the system reacts to in this step * @param <T> * the type of the class * @return the created user part of this step */ public <T> StepUserPart<T> user(Class<T> eventClass) { Objects.requireNonNull(eventClass); return new StepUserPart<>(stepPart, eventClass); }
.basicFlow() .step("S1").system(promptsUserToEnterName()) .step("S2").user(entersName()).system(greetsUser()) .step("S3").as(firstActor).user(entersName()).system(greetsUser()).reactWhile(someConditionIsFulfilled()) .step("S4").as(firstActor, secondActor).user(decidesToQuit()) .step("S5").as(firstActor, secondActor).system(promptsUserToEnterName()) .step("S6").system(quits()) .flow("Alternative flow A").insteadOf("S4") .step("S4c_2").continuesAt("S1") .flow("EX").anytime() .step("EX1").on(Exception.class).system(logsException()) .build();
@Test public void twoSequentialStepsReactWhenOneIsUserStepAndOtherIsSystemStep() { Model model = modelBuilder .useCase(USE_CASE) .basicFlow() .step(CUSTOMER_ENTERS_TEXT).user(EntersText.class) .step(SYSTEM_DISPLAYS_TEXT).system(displaysConstantText()) .build(); Optional<Step> latestStepRun = modelRunner.run(model).reactTo(entersText()); assertTrue(latestStepRun.map(step -> step.getName().equals(SYSTEM_DISPLAYS_TEXT)).orElse(false)); assertRecordedStepNames(CUSTOMER_ENTERS_TEXT, SYSTEM_DISPLAYS_TEXT); }
/** * Defines an "autonomous system reaction", meaning the system will react * without needing an event provided via {@link ModelRunner#reactTo(Object)}. * Instead, the model runner provides itself as an event to the system reaction. * * @param modelRunnerConsumer * the autonomous system reaction (that needs information from the model runner to work) * @return the created system part of this step */ StepSystemPart<ModelRunner> system(Consumer<ModelRunner> autonomousSystemReaction) { StepSystemPart<ModelRunner> systemPart = user(ModelRunner.class).system(autonomousSystemReaction); return systemPart; }
/** * Creates a new step in this flow, with the specified name, that follows the * current step in sequence. * * @param stepName * the name of the step to be created * @return the newly created step * @throws ElementAlreadyInModel * if a step with the specified name already exists in the use case */ public StepPart step(String stepName) { return system(new IgnoresIt<T>()).step(stepName); } }
public Model buildWith(ModelBuilder modelBuilder) { Model model = modelBuilder.useCase("Feel Stuff") .basicFlow() .step("1").user(asksForPoem).system(feelStuffUseCaseRealization::writesSadPoem) .step("2").user(asksForPoem).system(feelStuffUseCaseRealization::writesHappyPoem) .step("3").user(asksForPoem).system(feelStuffUseCaseRealization::writesFunnyPoem) .build(); return model; } }
static Model model() { SaysHelloWorld saysHelloWorld = new SaysHelloWorld(); SaysHelloToUser saysHelloToUser = new SaysHelloToUser(); Model model = Model.builder().useCase("Say hello to world, then user") .basicFlow() .step("S1").user(ASKS_FOR_HELLO_WORLD).system(saysHelloWorld) .step("S2").user(ASKS_FOR_HELLO_TO_USER).system(saysHelloToUser) .build(); return model; }
public Model buildWith(ModelBuilder modelBuilder) { Model model = modelBuilder.useCase("Get greeted") .basicFlow() .step("S1").system(this::promptsUserToEnterFirstName) .step("S2").user(ENTERS_FIRST_NAME).system(this::savesFirstName) .step("S3").system(this::promptsUserToEnterAge) .step("S4").user(ENTERS_AGE).system(this::savesAge) .step("S5").system(this::greetsUserWithFirstNameAndAge) .build(); return model; }
public Model buildWith(ModelBuilder modelBuilder) { Model model = modelBuilder.useCase("Get greeted") .basicFlow() .step("S1").system(this::promptsUserToEnterFirstName) .step("S2").user(ENTERS_FIRST_NAME).system(this::greetsUserWithFirstName) .build(); return model; }
@Test public void oneStepCanReactIfEventIsRight() { Model model = modelBuilder.useCase(USE_CASE) .basicFlow() .step(CUSTOMER_ENTERS_TEXT).user(EntersText.class).system(displaysEnteredText()).build(); modelRunner.run(model); boolean canReact = modelRunner.canReactTo(entersText().getClass()); assertTrue(canReact); Set<Step> stepsThatCanReact = modelRunner.getStepsThatCanReactTo(entersText().getClass()); assertEquals(1, stepsThatCanReact.size()); assertEquals(CUSTOMER_ENTERS_TEXT, stepsThatCanReact.iterator().next().getName().toString()); }
@Test public void singleEventTypeReactedTo() { Model model = modelBuilder.useCase(USE_CASE) .basicFlow() .step(CUSTOMER_ENTERS_TEXT).user(EntersText.class).system(displaysEnteredText()).build(); modelRunner.run(model); Set<Class<?>> reactToTypes = modelRunner.getReactToTypes(); assertEquals(1, reactToTypes.size()); Class<?> eventTypeReactedTo = reactToTypes.iterator().next(); assertEquals(EntersText.class, eventTypeReactedTo); }
public Model buildWith(ModelBuilder modelBuilder) { Model model = modelBuilder.useCase("Get greeted") .basicFlow() .step("S1").system(this::promptsUserToEnterFirstName) .step("S2").user(ENTERS_FIRST_NAME).system(this::savesFirstName) .step("S3").system(this::promptsUserToEnterAge) .step("S4").user(ENTERS_AGE).system(this::savesAge) .step("S5").system(this::greetsUserWithFirstNameAndAge) .step("S6").system(this::stops) .flow("Handle out-of-bounds age").insteadOf("S5").condition(this::ageIsOutOfBounds) .step("S5a_1").system(this::informsUserAboutOutOfBoundsAge) .step("S5a_2").continuesAt("S3") .flow("Handle non-numerical age").insteadOf("S5") .step("S5b_1").on(NON_NUMERICAL_AGE).system(this::informsUserAboutNonNumericalAge) .step("S5b_2").continuesAt("S3") .build(); return model; }
@Test public void throwsExceptionIfMoreThanOneStepCanReactInDifferentUseCases() { thrown.expect(MoreThanOneStepCanReact.class); thrown.expectMessage("Step 1"); thrown.expectMessage("Step 2 with same event as Step 1"); Model model = modelBuilder .useCase("Use Case") .basicFlow() .step("Step 1").user(String.class).system(s -> System.out.println(s)) .useCase("Another Use Case") .basicFlow() .step("Step 2 with same event as Step 1").user(String.class).system(s -> System.out.println(s)) .build(); modelRunner.run(model); modelRunner.reactTo(new String("Some text")); }
@Test public void sameEventTypeReactedTo() { Model model = modelBuilder.useCase(USE_CASE) .basicFlow().anytime() .step(CUSTOMER_ENTERS_TEXT).user(EntersText.class).system(displaysEnteredText()) .flow("Alternative Flow: Could react as well").anytime() .step(CUSTOMER_ENTERS_ALTERNATIVE_TEXT).user(EntersText.class).system(displaysEnteredText()) .build(); modelRunner.run(model); Set<Class<?>> reactToTypes = modelRunner.getReactToTypes(); assertEquals(1, reactToTypes.size()); Class<?> eventTypeReactedTo = reactToTypes.iterator().next(); assertEquals(EntersText.class, eventTypeReactedTo); }
@Test public void actorsCanBeReusedInUseCase() { UseCasePart useCasePart = modelBuilder.useCase(USE_CASE); Model model = useCasePart.basicFlow() .step(CUSTOMER_ENTERS_TEXT).as(customer).user(EntersText.class).system(displaysEnteredText()) .step(CUSTOMER_ENTERS_TEXT_AGAIN).as(customer).user(EntersText.class).system(displaysEnteredText()) .build(); Collection<Step> steps = model.getSteps(); assertEquals(2, steps.size()); Iterator<Step> stepsIt = steps.iterator(); Actor actor1 = stepsIt.next().getActors()[0]; Actor actor2 = stepsIt.next().getActors()[0]; assertTrue(actor1 == actor2); assertEquals(customer, actor1); }
@Test public void doesNotReactToEmptyEventList() { Model model = modelBuilder .useCase(USE_CASE) .basicFlow() .step(CUSTOMER_ENTERS_TEXT).user(EntersText.class).system(displaysEnteredText()) .build(); Optional<Step> latestStepRun = modelRunner.run(model).reactTo(new ArrayList<Object>()); assertFalse(latestStepRun.isPresent()); assertRecordedStepNames(new String[0]); }
@Test public void eventTypesReactedOnlyIfConditionFulfilled() { Model model = modelBuilder.useCase(USE_CASE) .basicFlow().condition(() -> false) .step(CUSTOMER_ENTERS_TEXT).user(EntersText.class).system(displaysEnteredText()) .flow("Alternative Flow: Could react as well").anytime() .step(CUSTOMER_ENTERS_NUMBER).user(EntersNumber.class).system(displaysEnteredNumber()) .build(); modelRunner.run(model); Set<Class<?>> reactToTypes = modelRunner.getReactToTypes(); assertEquals(1, reactToTypes.size()); Class<?> eventTypeReactedTo = reactToTypes.iterator().next(); assertEquals(EntersNumber.class, eventTypeReactedTo); }
@Test public void differentEventTypesReactedTo() { Model model = modelBuilder.useCase(USE_CASE) .basicFlow().anytime() .step(CUSTOMER_ENTERS_TEXT).user(EntersText.class).system(displaysEnteredText()) .flow("Alternative Flow: Could react as well").anytime() .step(CUSTOMER_ENTERS_NUMBER).user(EntersNumber.class).system(displaysEnteredNumber()) .build(); modelRunner.run(model); Set<Class<?>> reactToTypes = modelRunner.getReactToTypes(); assertEquals(2, reactToTypes.size()); assertTrue(reactToTypes.contains(EntersText.class)); assertTrue(reactToTypes.contains(EntersNumber.class)); }
@Test public void createsSingleActorWithSingleUseCase() { UseCasePart useCasePart = modelBuilder.useCase(USE_CASE); Model model = useCasePart.basicFlow() .step(CUSTOMER_ENTERS_TEXT).as(customer).user(EntersText.class).system(displaysEnteredText()) .build(); Actor customerActor = model.findActor(CUSTOMER); Set<UseCase> useCases = customerActor.getUseCases(); assertEquals(1, useCases.size()); UseCase actualUseCase = useCases.iterator().next(); assertEquals(USE_CASE, actualUseCase.getName()); }