Как создать StateMachine из StateMachineConfigurer - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть конфигурация конечного автомата на основе аннотаций:

@Component @Scope(BeanDefinition.SCOPE_PROTOTYPE)
@EnableStateMachine(name = "machine1")
public class Machine1 extends
   EnumStateMachineConfigurerAdapter<SimStates, SimEvents> {

   @Override
   public void configure(StateMachineStateConfigurer<SimStates, SimEvents> states) throws Exception {
      states.withStates()
        .initial(INIT)
        .state(INIT)
        .state(S1)
        .state(FINISH)
        .end(FINISH)
      ;
    }
  ...

Теперь я хочу создать для нее тесты.Я бы предпочел, чтобы не было неявного вызова getBean("machine1") "через StateMachineFactory.getStateMachine("machine1"), что потребовало бы контекста приложения.

Я бы предпочел создать экземпляр Machine1 и передать его какому-нибудь Builder, Configurator или Adapter дляполучить StateMachine<> экземпляр.

public class Machine1Test {

  @Test
  public void testMachine1() throws Exception {

    final StateMachineConfigurer<SimStates, SimEvents> smc = 
      new Machine1();


    final StateMachineBuilder.Builder<SimStates, SimEvents> builder = 
        StateMachineBuilder.builder();

    // can I use the builder together with smc? Or something else?

    StateMachine<SimStates,SimEvents> sm = ... // how?
  }
}

Обновление: Я обновил "без полного контекста приложения" до "без неявного вызова getBean("machine1")". Вопрос также о пониманииобо всех фабриках, адаптерах, конфигурациях и конфигураторах пружинного автомата.

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

Я не нашел явного способа использования EnumStateMachineConfigurerAdapter с StateMachineBuilder.Builder<>, но я использовал этот подход:

@Component 
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@EnableStateMachine(name = "machine1")
public class Machine1 extends EnumStateMachineConfigurerAdapter<SimStates, SimEvents> {

    @Override
    public void configure(StateMachineStateConfigurer<SimStates, SimEvents> states) throws Exception {
        configureStates(states);
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<SimStates, SimEvents> transitions) throws Exception {
        configureTransitions(transitions);
    }

    public static void configureStates(StateMachineStateConfigurer<SimStates, SimEvents> states) throws Exception {
        states.withStates()
                .initial(INIT)
                .state(INIT)
                .state(S1)
                .state(FINISH)
                .end(FINISH);
    }

    public static void configureTransitions(StateMachineTransitionConfigurer<SimStates, SimEvents> states) throws Exception {
        states.withTransitions()
                // configure transitions
        ;
    }
}

и импорт методов статической конфигурации в тесте Statemachine:

import static com.example.statemachine.Machine1.configureStates;
import static com.example.statemachine.Machine1.configureTransitions;

public class TestEventNotAccepted {

    @Test
    public void testEventNotAccepted() throws Exception {
        StateMachine<SimStates, SimEvents> machine = buildMachine();
        StateMachineTestPlan<SimStates, SimEvents> plan =
                StateMachineTestPlanBuilder.<SimStates, SimEvents>builder()
                        .defaultAwaitTime(2)
                        .stateMachine(machine)

                        .step()
                        .expectStates(INIT)
                        .and()

                        // configure other test steps

                        .build();
        plan.test();
    }

    private StateMachine<SimStates, SimEvents> buildMachine() throws Exception {
        StateMachineBuilder.Builder<SimStates, SimEvents> builder = StateMachineBuilder.builder();

        builder.configureConfiguration()
                .withConfiguration()
                .taskExecutor(new SyncTaskExecutor())
                .listener(customListener())
                .autoStartup(true);

        configureStates(builder.configureStates());

        configureTransitions(builder.configureTransitions());

        return builder.build();
    }
}

В результате я смог провести модульное тестирование моей точной конфигурации без построения всего контекста Spring и без использования @SpringBootTest.

0 голосов
/ 23 февраля 2019

Я бы предпочел создать экземпляр Machine1 и передать его какому-нибудь Builder, Configurator или Adapter, чтобы получить экземпляр StateMachine <>.

Spring State Mahcine поддерживает аннотацииоснованная конфигурация для создания экземпляров (например, через адаптер ) или Builder - других вариантов нет.

SM через адаптер

Использование @SpringBootTest(clasess = <YourEnumSMConfig> определенно не создает полный контекст приложения:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = { Machine1.class})
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class StateMachineTests {

    @Autowired
    private StateMachine<String, String> machine1;

    @Test
    public void testInitialState() throws Exception {
        StatMachineTestPlan<SimState, SimEvent> plan = StateMachineTestPlanBuilder.<SimState, SimEvent>builder()
          .defaultAwaitTime(2)
          .stateMachine(machine1)
          .step()
            .expectStateChange(1)
            .expectStateEntered(SimState.INITIAL)
            .expectState(SimState.INITIAL)
          .and()
          .build()

      plan.test();
    }

}

Теперь я хочу создать тестыдля него.

Тестирование с TestPlanBuilder:

Из коробки есть поддержка тестирования для тестирования конечного автомата пружины.Это называется StateMachineTestPlan.Вы можете построить StateMachineTestPlan, используя StateMachineTestPlanBuilder.

Доступ к этим классам вы можете получить, объявив следующую зависимость:

<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-test</artifactId>
    <version>2.0.3.RELEASE</version>  // change version to match yours
    <scope>test</scope>
</dependency>

Подробная официальная документация относительнотестирование доступно здесь .

SM через Builder

Я бы предпочел не иметь неявного вызова getBean ("machine1")"через StateMachineFactory.getStateMachine (" machine1 "), для которого потребуется контекст приложения.

Создание SM с помощью Builder не требует любого контекст Spring.

public class TestEventNotAccepted {

    @Test
    public void testEventNotAccepted() throws Exception {
        StateMachine<String, String> machine = buildMachine();
        StateMachineTestPlan<String, String> plan =
                StateMachineTestPlanBuilder.<String, String>builder()
                        .defaultAwaitTime(2)
                        .stateMachine(machine)
                        .step()
                        .expectStates("SI")
                        .and()
                        .step()
                        .sendEvent("E2")
                        .and()
                        .build();
        plan.test();
    }

    private StateMachine<String, String> buildMachine() throws Exception {
        StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder();

        builder.configureConfiguration()
                .withConfiguration()
                .taskExecutor(new SyncTaskExecutor())
                .listener(customListener())
                .autoStartup(true);

        builder.configureStates()
                .withStates()
                .initial("SI")
                .state("S1")
                .state("S2");

        builder.configureTransitions()
                .withExternal()
                .source("SI").target("S1")
                .event("E1")
                .action(c -> c.getExtendedState().getVariables().put("key1", "value1"))
                .and()
                .withExternal()
                .source("S1").target("S2").event("E2");

        return builder.build();
    }

    private StateMachineListener<String, String> customListener() {
        return new StateMachineListenerAdapter<String, String>() {
            @Override
            public void eventNotAccepted(Message event) {
                System.out.println("EVENT NOT ACCEPTED: " + event);
            }
        };
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...