Adsense

2018年1月20日土曜日

Spring Statemachineを使ってドラクエ11のポーカーを自動プレイするプログラムを作ってロイヤルストレートスライムを出すまでの道のり 第5回

※ ツールのソースコードはGitHubにあがっていますので参照してください。

さて、前回は状態遷移図を作って、Configクラスで状態遷移マシンの定義を行うところまでやりました。

今回は、前回作成した状態遷移マシンをテストしてみるところからはじめます。

spring-statemachine-testの設定

pom.xmlで下記の依存関係が定義されていない場合は追加します。


        <dependency>
            <groupId>org.springframework.statemachine</groupId>
            <artifactId>spring-statemachine-test</artifactId>
            <version>1.2.7.RELEASE</version>
            <scope>test</scope>
        </dependency>


テストコード

テストコードはこんな感じになります。

@RunWith(SpringRunner.class)
@SpringBootTest(properties = "mode=TEST")
public class AutoplayStatemachineTest {

    @Autowired
    StateMachine<States, Events> stateMachine;

    @MockBean
    Robot robot;

    @MockBean
    ActivateWindowUtil activateWindowUtil;

    @Before
    public void setUp() {
        when(robot.createScreenCapture(anyObject()))
            .thenReturn(new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB));
        doNothing().when(activateWindowUtil).activate();
    }

    @Test
    public void testStateMachine01() throws Exception {
        List<Card> cards = Arrays.asList(Card.S10, Card.SJ, Card.SQ, Card.XX, Card.XX);
        Message<Events> dealCardsEventMessage = MessageBuilder
                .withPayload(Events.DEAL_CARDS_EVENT)
                .setHeader("cards", cards)
                .build();

        StateMachineTestPlanBuilder.<States, Events>builder()
            .stateMachine(stateMachine)
            .step()
                .expectStates(PLAYING_POKER_STATE, OTHER_STATE)
                .and()
            .step()
                .sendEvent(dealCardsEventMessage)
                .expectStateChanged(1)
                .expectStates(PLAYING_POKER_STATE, DEALT_CARDS_STATE)
                .and()
            .step()
                .sendEvent(OTHER_EVENT)
                .expectStateChanged(1)
                .expectStates(PLAYING_POKER_STATE, OTHER_STATE)
                .and()
            .step()
                .sendEvent(OTHER_EVENT)
                .expectStateChanged(1)
                .expectStates(PLAYING_POKER_STATE, OTHER_STATE)
                .and()
            .step()
                .sendEvent(ROYAL_STRAIGHT_SLIME_EVENT)
                .expectStateChanged(1)
                .expectState(FINAL_STATE)
                .expectStateMachineStopped(2)
                .and()
            .build()
            .test();
    }

まず、6-7行目でStateMachineをインジェクションしています。

9-20行目ではモック化の設定を行っていますが、これはSpring Statemachineとは直接関係がない処理なので、あまり気にしなくても大丈夫です。今回のツールでは状態遷移に伴うActionの中でキーボードやマウスの操作等を行っていますが、テスト中にそのような操作が発生してしまうと困るため、操作を行うクラスをモック化して回避しています。

30行目のStateMachineTestPlanBuilderがspring-statemachine-testの肝となるクラスです。このBuilderに対してメソッドチェーンでstepを定義してきます。step内では、状態遷移マシンに送信するイベントと、その結果期待される状態遷移マシンの状態を定義していきます。

StateMachineTestPlanBuilderで使用できる主なメソッドは以下になります。詳細はJavadocをご参照ください。
  • sendEvent(event): 状態遷移マシンにイベントを送信する
  • expectState(state): 期待する状態
  • expectStates(state...): 期待する状態(複数)。状態に親子関係がある場合はこちらを使う
  • expectStateChanged(count): 期待する状態遷移の発生回数。
  • expectStateMachineStopped(count): 状態遷移マシンが停止した回数。
expectStateMachineStoppedメソッドについて、状態遷移マシンが複数回停止することあるの?と思われるかもしれませんが、どうやら状態が親子関係にある場合、親状態内の状態遷移は別の状態遷移マシンとしてカウントされるようです。上記ソースコードでは、PLAYING_POKER_STATE内の状態遷移マシンと、全体の状態遷移マシンが停止するのでcountを2にしています。

テスト結果

テストを実行してみます。
成功してもつまらないので、上記コードの37行目でexpectStateChangedを1から2に変更して、テストを失敗させてみます。実行結果は下記の通り。

java.lang.AssertionError: StateChanged Await not matched for machine FINAL_STATE PLAYING_POKER_STATE DEALT_CARDS_STATE OTHER_STATE RETRY_OR_END_STATE  / PLAYING_POKER_STATE,DEALT_CARDS_STATE / uuid=b1ae81c3-d178-4ff0-8465-228b380a5500 / id=null
Expected: is <true>
     but: was <false>
 at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
 at org.junit.Assert.assertThat(Assert.java:956)
 at org.springframework.statemachine.test.StateMachineTestPlan.test(StateMachineTestPlan.java:167)
 at com.github.lenemarix.autoplay.dq11.poker.statemachine.AutoplayStatemachineTest.testStateMachine01(AutoplayStatemachineTest.java:92)
...(以下略)...

うーん、どうやら2回めに発生するはずの状態変化を待ったが結局発生しなかったのでエラーになったという事のようですが・・・。
一体全体、どこのexpectで失敗したのか分からないですね・・・。expectStatechangedで整数を指定したはずなのに、Expectedやbutの値がbooleanになってるし。

ちなみに、スタックトレース内のAutoplayStatemachineTest.java:92は、上記テストコードの57行目のtest()メソッド呼び出しを指しています。例外発生は全てtest()メソッドになってしまうので、スタックトレースが役に立ちませんね・・・。

ちょっと使うには辛い印象。

0 件のコメント:

コメントを投稿