From 7f78deadcc56e9ad7315e97a4cd1b71888ed3817 Mon Sep 17 00:00:00 2001 From: jvalkeal Date: Sat, 27 Jan 2018 14:30:54 +0000 Subject: [PATCH] Add hamcrest integration to variables in StateMachineTestPlanBuilder - For testing extended state variables, it's now possible to use new method expectVariableMatcher which takes hamcrest matcher as an argument. - Fixes #483 --- docs/src/reference/asciidoc/sm.adoc | 7 ++ .../test/StateMachineTestPlan.java | 11 ++- .../test/StateMachineTestPlanBuilder.java | 30 +++++-- .../test/StateMachineTestingTests.java | 80 ++++++++++++++++++- .../test/docs/DocsTestSampleTests.java | 20 ++++- 5 files changed, 138 insertions(+), 10 deletions(-) diff --git a/docs/src/reference/asciidoc/sm.adoc b/docs/src/reference/asciidoc/sm.adoc index 7b2a8b22..5d1b8c79 100644 --- a/docs/src/reference/asciidoc/sm.adoc +++ b/docs/src/reference/asciidoc/sm.adoc @@ -2064,6 +2064,13 @@ state machine features and multiple machines can be added to a plan. If multiple machines are added then it is also possible to choose if event is sent to particular, random or all machines. +Above testing example uses hamcrest imports: + +[source,java,indent=0] +---- +include::samples/DocsTestSampleTests.java[tags=snippetC] +---- + [TIP] ==== All possible options for expected are documented in javadocs diff --git a/spring-statemachine-test/src/main/java/org/springframework/statemachine/test/StateMachineTestPlan.java b/spring-statemachine-test/src/main/java/org/springframework/statemachine/test/StateMachineTestPlan.java index 7c9a3c80..113577fa 100644 --- a/spring-statemachine-test/src/main/java/org/springframework/statemachine/test/StateMachineTestPlan.java +++ b/spring-statemachine-test/src/main/java/org/springframework/statemachine/test/StateMachineTestPlan.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -268,6 +268,15 @@ public class StateMachineTestPlan { } } + if (!step.expectVariableKeysMatchers.isEmpty()) { + for (StateMachine stateMachine : stateMachines.values()) { + Map variables = stateMachine.getExtendedState().getVariables(); + for (Matcher> matcher : step.expectVariableKeysMatchers) { + org.hamcrest.MatcherAssert.assertThat(variables, matcher); + } + } + } + if (!step.expectVariables.isEmpty()) { for (StateMachine stateMachine : stateMachines.values()) { Map variables = stateMachine.getExtendedState().getVariables(); diff --git a/spring-statemachine-test/src/main/java/org/springframework/statemachine/test/StateMachineTestPlanBuilder.java b/spring-statemachine-test/src/main/java/org/springframework/statemachine/test/StateMachineTestPlanBuilder.java index 2aa8449c..170f1660 100644 --- a/spring-statemachine-test/src/main/java/org/springframework/statemachine/test/StateMachineTestPlanBuilder.java +++ b/spring-statemachine-test/src/main/java/org/springframework/statemachine/test/StateMachineTestPlanBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.hamcrest.Matcher; import org.springframework.messaging.Message; import org.springframework.statemachine.StateMachine; @@ -129,9 +130,9 @@ public class StateMachineTestPlanBuilder { Integer expectStateMachineStopped; Integer expectExtendedStateChanged; final Collection expectVariableKeys = new ArrayList(); + final Collection>> expectVariableKeysMatchers = new ArrayList<>(); final Map expectVariables = new HashMap(); - /** * Expect a state {@code S}. * @@ -249,6 +250,7 @@ public class StateMachineTestPlanBuilder { * by {@code machineId}. Multiple events can be defined which are then * send in defined order. * + * * @param event the event * @param machineId the machine identifier for sending event * @return the state machine test plan step builder @@ -270,6 +272,17 @@ public class StateMachineTestPlanBuilder { return this; } + /** + * Expect variable map with hamcrest {@link Matcher}. + * + * @param matcher the matcher + * @return the state machine test plan step builder + */ + public StateMachineTestPlanStepBuilder expectVariableMatcher(Matcher> matcher) { + this.expectVariableKeysMatchers.add(matcher); + return this; + } + /** * Expect variable to exist in extended state variables and match * with the value. @@ -458,8 +471,9 @@ public class StateMachineTestPlanBuilder { steps.add(new StateMachineTestPlanStep(sendEvent, sendMessage, sendEventMachineId, sendEventToAll, sendEventParallel, expectStates, expectStateChanged, expectStateEntered, expectStateExited, expectEventNotAccepted, expectTransition, expectTransitionStarted, expectTransitionEnded, - expectStateMachineStarted, expectStateMachineStopped, expectVariableKeys, expectVariables, - expectExtendedStateChanged, expectStatesEntrered, expectStatesExited)); + expectStateMachineStarted, expectStateMachineStopped, expectVariableKeys, + expectVariableKeysMatchers, expectVariables, expectExtendedStateChanged, + expectStatesEntrered, expectStatesExited)); return StateMachineTestPlanBuilder.this; } @@ -485,6 +499,7 @@ public class StateMachineTestPlanBuilder { Integer expectStateMachineStopped; Integer expectExtendedStateChanged; final Collection expectVariableKeys; + final Collection>> expectVariableKeysMatchers; final Map expectVariables; public StateMachineTestPlanStep(List sendEvent, List> sendMessage, Object sendEventMachineId, @@ -492,8 +507,10 @@ public class StateMachineTestPlanBuilder { Integer expectStateChanged, Integer expectStateEntered, Integer expectStateExited, Integer expectEventNotAccepted, Integer expectTransition, Integer expectTransitionStarted, Integer expectTransitionEnded, Integer expectStateMachineStarted, Integer expectStateMachineStopped, - Collection expectVariableKeys, Map expectVariables, - Integer expectExtendedStateChanged, Collection expectStatesEntrered, Collection expectStatesExited) { + Collection expectVariableKeys, Collection>> expectVariableKeysMatchers, + Map expectVariables, + Integer expectExtendedStateChanged, Collection expectStatesEntrered, + Collection expectStatesExited) { this.sendEvent = sendEvent; this.sendMessage = sendMessage; this.sendEventMachineId = sendEventMachineId; @@ -510,6 +527,7 @@ public class StateMachineTestPlanBuilder { this.expectStateMachineStarted = expectStateMachineStarted; this.expectStateMachineStopped = expectStateMachineStopped; this.expectVariableKeys = expectVariableKeys; + this.expectVariableKeysMatchers = expectVariableKeysMatchers; this.expectVariables = expectVariables; this.expectExtendedStateChanged = expectExtendedStateChanged; this.expectStatesEntrered = expectStatesEntrered; diff --git a/spring-statemachine-test/src/test/java/org/springframework/statemachine/test/StateMachineTestingTests.java b/spring-statemachine-test/src/test/java/org/springframework/statemachine/test/StateMachineTestingTests.java index b8028121..9ccf8ea1 100644 --- a/spring-statemachine-test/src/test/java/org/springframework/statemachine/test/StateMachineTestingTests.java +++ b/spring-statemachine-test/src/test/java/org/springframework/statemachine/test/StateMachineTestingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,9 @@ */ package org.springframework.statemachine.test; +import static org.hamcrest.CoreMatchers.not; + +import org.hamcrest.collection.IsMapContaining; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -105,6 +108,48 @@ public class StateMachineTestingTests extends AbstractStateMachineTests { plan.test(); } + @SuppressWarnings("unchecked") + @Test + public void testVariables() throws Exception { + registerAndRefresh(Config5.class); + StateMachine machine = context.getBean(StateMachine.class); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(machine) + .step().expectStateMachineStarted(1).and() + .step().expectState("SI").and() + .step() + .sendEvent("E1") + .expectStateChanged(1) + .expectState("S1") + .expectVariable("V1Key") + .expectVariable("V1Key", "V1Value") + .expectVariableMatcher(IsMapContaining.hasKey("V1Key")) + .expectVariableMatcher(IsMapContaining.hasValue("V1Value")) + .expectVariableMatcher(IsMapContaining.hasEntry("V1Key", "V1Value")) + .expectVariableMatcher(not(IsMapContaining.hasKey("V2Key"))) + .and() + .step() + .sendEvent("E2") + .expectStateChanged(1) + .expectState("S2") + .expectVariable("V1Key") + .expectVariable("V1Key", "V1Value") + .expectVariable("V2Key") + .expectVariable("V2Key", "V2Value") + .expectVariableMatcher(IsMapContaining.hasKey("V1Key")) + .expectVariableMatcher(IsMapContaining.hasValue("V1Value")) + .expectVariableMatcher(IsMapContaining.hasEntry("V1Key", "V1Value")) + .expectVariableMatcher(IsMapContaining.hasKey("V2Key")) + .expectVariableMatcher(IsMapContaining.hasValue("V2Value")) + .expectVariableMatcher(IsMapContaining.hasEntry("V2Key", "V2Value")) + .and() + .build(); + + plan.test(); + } + @Override protected AnnotationConfigApplicationContext buildContext() { return new AnnotationConfigApplicationContext(); @@ -246,4 +291,37 @@ public class StateMachineTestingTests extends AbstractStateMachineTests { } + @Configuration + @EnableStateMachine + static class Config5 extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial("SI") + .state("S1") + .state("S2"); + } + + @Override + public void configure(StateMachineTransitionConfigurer transitions) throws Exception { + transitions + .withExternal() + .source("SI") + .target("S1") + .event("E1") + .action(c -> { + c.getExtendedState().getVariables().put("V1Key", "V1Value"); + }) + .and() + .withExternal() + .source("S1") + .target("S2") + .event("E2") + .action(c -> { + c.getExtendedState().getVariables().put("V2Key", "V2Value"); + }); + } + } } diff --git a/spring-statemachine-test/src/test/java/org/springframework/statemachine/test/docs/DocsTestSampleTests.java b/spring-statemachine-test/src/test/java/org/springframework/statemachine/test/docs/DocsTestSampleTests.java index 33945517..f565f51f 100644 --- a/spring-statemachine-test/src/test/java/org/springframework/statemachine/test/docs/DocsTestSampleTests.java +++ b/spring-statemachine-test/src/test/java/org/springframework/statemachine/test/docs/DocsTestSampleTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,13 @@ */ package org.springframework.statemachine.test.docs; +//tag::snippetC[] +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.collection.IsMapContaining.hasKey; +import static org.hamcrest.collection.IsMapContaining.hasValue; +import static org.hamcrest.collection.IsMapContaining.hasEntry; +//end::snippetC[] + import org.junit.Test; import org.springframework.core.task.SyncTaskExecutor; import org.springframework.statemachine.StateMachine; @@ -39,6 +46,12 @@ public class DocsTestSampleTests { .sendEvent("E1") .expectStateChanged(1) .expectStates("S1") + .expectVariable("key1") + .expectVariable("key1", "value1") + .expectVariableMatcher(hasKey("key1")) + .expectVariableMatcher(hasValue("value1")) + .expectVariableMatcher(hasEntry("key1", "value1")) + .expectVariableMatcher(not(hasKey("key2"))) .and() .build(); plan.test(); @@ -62,7 +75,10 @@ public class DocsTestSampleTests { builder.configureTransitions() .withExternal() .source("SI").target("S1") - .event("E1"); + .event("E1") + .action(c -> { + c.getExtendedState().getVariables().put("key1", "value1"); + }); return builder.build(); }