Announcement Announcement Module
Collapse
No announcement yet.
AbstractManagedFlowExecutionTests is GONE... :-( Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • AbstractManagedFlowExecutionTests is GONE... :-(

    Keith, what was the reason for the removal of that class. It was a very nice hookup to be able to wire up all the sub-flows and the spring-beans.xml file and do a full flow test without having to mock anything other than use some of the mock buz. services via injection?

    What is the suggested alternative to be able to provide the list of flow xml files as well as the dispatcher-servlet.xml file with the bean configs?

    Alex.

  • #2
    Alex,

    The suggested alternative is to develop your flow execution test without a dependency on the container, using a test-specific FlowArtifactFactory to return the artifacts (actions and mock services) needed by your flow to complete each of your test scenarios.

    I agree as it was was useful, but that approach imposed a dependency on spring-mock and Spring IoC just to test a flow execution. Flow execution testing should be doable without any such dependency (other than standard JUnit), to test the execution and associated flow artifacts in isolation, and to allow for easier testing with dynamic mocks.

    For a example of this strategy see both the test cases for Phonebook and Sellitem. There is also a section in the reference manual that demonstrates this approach.

    We should probably also add back support for full blown flow execution integration tests that involve Spring IoC as an additional test support option--that just shouldn't be the only option, and there should be no required dependency on spring-mock's AbstractTransactionalDataSourceSpringContextTests just to test a flow.

    Keith

    Comment


    • #3
      Originally posted by Keith Donald
      We should probably also add support for full blown flow execution integration tests that involve Spring IoC as an additional test support option, just not the only one.
      Yes, that would be very usefull, as it is exactly the purpose we are using it for. Untill that has been added, can you suggest what would be the easiest way to wire the whole thing up? I am having difficulties linking together the XmlFlowBuilders with FlowRegistry and sending it all into FlowRegistryFlowArtifactFactory.

      Thanks,
      Alex

      Comment


      • #4
        Take a look at Phonebook's and Sellitem's sample tests. Phonebook even demonstrates testing the search-flow in isolation with a mock detail flow simulating end state scenarios.

        Keith

        Comment


        • #5
          I guess I will be sticking to my previous SWF build for awhile. All my tests, so far, were integration tests. I understand the reasoning behind the removal, but Wow! That class was was extremely helpful.

          Will be checking out the updated SWF sample tests.

          _Curtney

          Comment


          • #6
            Well, it's understandable that they were helpful, because that's all that was provided. :-)

            Still, the dependency on spring-mock for this was not warranted, and was something I was never happy with. There is simply no reason to make so many assumptions for typical flow execution tests, and I've come to realize this more as I've written more tests (and particularly tests that test subflow interaction, where the subflows are effectively stubbed and service-layer interaction is mocked).

            I understand it is helpful and there is a valid case for testing with the container, so we'll add this back for 1.0 rc1. I appreciate the feedback!

            I still highly recommend studying the revised approach to testing for both Phonebook and Sellitem, as I would recommend that as the preferred approach to testing most flow executions.

            Keith

            Comment


            • #7
              Providing flowScope objects to a test flow ?

              Keith,

              having looked at the samples (I hope I didn't miss anything there) I have a question.

              I am trying to test a 'black-box' sub-flow that expects (contract style) to find a specific 'beanAbc' in its FlowScope. This beanAbc is passed in by the calling flow using <input-mapping>.

              My question is, how do I start the test fixture flow with a beanAbc already in its FlowScope ?

              I am using an inner class (as per samples):

              protected class TestFlowArtifactFactory extends FlowArtifactFactoryAdapter

              inside my

              public class GeneralSubflowTest extends AbstractXmlFlowExecutionTests

              test class.

              If I use getFlowExecutionContext().getActiveSession() to try and get access to getScope().setAttribute("beanAbc", fixtureBeanAbc) I get an IllegalStateException because the flow hasn't started.
              After I call startFlow() it is too late since the first state of the flow tests for the existence of beanAbc in its FlowScope.

              It seems to me that I might be missing either a startFlow(FlowSession) or a preFlowStart() type hook in the AbstractXmlFlowExecutionTests hierarchy?

              Or have I missed something else ?

              Comment


              • #8
                David,

                You need to use a startFlow variant that accepts a FlowExecutionListener--in your listener (extending FlowExecutionListenerAdapter) override sessionStarting to put the bean in the input map.

                Keith

                Comment


                • #9
                  Update:

                  We've introduced a MockFlowArtifactFactory that makes it easier to test a FlowExecution that depends on externally managed flow artifacts, such as subflows, actions (or services wrapped by actions), etc.

                  It works by allowing programmatic registration of these artifacts when the test is setup. Basically you can setup the flow artifact factory on a test by test method basis, without having to load any XML context files (as bean registration is done programatically). This is ideal for registering mocks or stubs of your business services, which is typically what you want for a flow execution unit test.

                  Here's the class and example usage:

                  Code:
                  /**
                   * A stub flow artifact factory implementation suitable for a test environment.
                   * <p>
                   * Allows programmatic registration of subflows needed by a flow execution being
                   * tested, see {@link #registerSubFlow(Flow)}.
                   * <p>
                   * Also supports programmatic registration of additional custom artifacts needed
                   * by a flow (such as Actions) managed in a backing Spring {@link ConfigurableBeanFactory};
                   * see {@link #registerBean(String, Object)}.  Beans registered are typically mocks or 
                   * stubs of business services invoked by the flow.
                   * 
                   * @author Keith Donald
                   */
                  public class MockFlowArtifactFactory extends FlowRegistryFlowArtifactFactory {
                  
                  	/**
                  	 * Creates a new mock flow artifact factory.
                  	 */
                  	public MockFlowArtifactFactory() {
                  		super(new FlowRegistryImpl(), new StaticApplicationContext());
                  	}
                  
                  	/**
                  	 * Register a subflow definition in this factory; typically to support a
                  	 * flow execution test.
                  	 * @param subflow the subflow
                  	 */
                  	public void registerSubFlow(Flow subflow) {
                  		getSubflowRegistry().registerFlow(new StaticFlowHolder(subflow));
                  	}
                  
                  	/**
                  	 * Register a bean in this factory; typically to support a flow execution
                  	 * test. If this bean is a service object used as an Action, it is often a
                  	 * stub or dynamic mock.
                  	 * @param beanName the bean name
                  	 * @param bean the singleton instance
                  	 */
                  	public void registerBean(String beanName, Object bean) {
                  		((StaticApplicationContext)getBeanFactory()).getBeanFactory().registerSingleton(beanName, bean);
                  	}
                  }
                  Code:
                  public class SearchFlowExecutionTests extends AbstractXmlFlowExecutionTests {
                  
                  	public void testStartFlow() {
                  		ViewSelection view = startFlow();
                  		assertCurrentStateEquals("enterCriteria");
                  		assertViewNameEquals("searchCriteria", view);
                  		assertModelAttributeNotNull("searchCriteria", view);
                  	}
                  
                  	public void testCriteriaSubmitSuccess() {
                  		startFlow();
                  		MockParameterMap parameters = new MockParameterMap();
                  		parameters.put("firstName", "Keith");
                  		parameters.put("lastName", "Donald");
                  		ViewSelection view = signalEvent("search", parameters);
                  		assertCurrentStateEquals("displayResults");
                  		assertViewNameEquals("searchResults", view);
                  		assertModelAttributeCollectionSize(1, "results", view);
                  	}
                  
                  	public void testCriteriaSubmitError() {
                  		startFlow();
                  		signalEvent("search");
                  		assertCurrentStateEquals("enterCriteria");
                  	}
                  
                  	public void testNewSearch() {
                  		testCriteriaSubmitSuccess();
                  		ViewSelection view = signalEvent("newSearch");
                  		assertCurrentStateEquals("enterCriteria");
                  		assertViewNameEquals("searchCriteria", view);
                  	}
                  
                  	public void testSelectValidResult() {
                  		testCriteriaSubmitSuccess();
                  		MockParameterMap parameters = new MockParameterMap();
                  		parameters.put("id", "1");
                  		ViewSelection view = signalEvent("select", parameters);
                  		assertCurrentStateEquals("displayResults");
                  		assertViewNameEquals("searchResults", view);
                  		assertModelAttributeCollectionSize(1, "results", view);
                  	}
                  
                  	/**
                  	 * A stub for testing.
                  	 */
                  	private PhoneBook phonebook = new ArrayListPhoneBook();
                  
                  	protected ExternalizedFlowDefinition getFlowDefinition() {
                  		File flowDir = new File("src/webapp/WEB-INF/flows");
                  		Resource resource = new FileSystemResource(new File(flowDir, "search-flow.xml"));
                  		return new ExternalizedFlowDefinition(resource);
                  	}
                  
                  	protected FlowArtifactFactory createFlowArtifactFactory() {
                  		MockFlowArtifactFactory flowArtifactFactory = new MockFlowArtifactFactory();
                  
                  		Flow detailFlow = new Flow("detail-flow");
                  		// test responding to finish result
                  		EndState finish = new EndState(detailFlow, "finish");
                  		finish.addEntryAction(new AbstractAction() {
                  			public Event doExecute(RequestContext context) throws Exception {
                  				// test attribute mapping
                  				assertEquals(new Long(1), context.getFlowScope().get("id"));
                  				return success();
                  			}
                  		});
                  		flowArtifactFactory.registerSubFlow(detailFlow);
                  		flowArtifactFactory.registerBean("phonebook", phonebook);
                  		return flowArtifactFactory;
                  	}
                  }

                  Comment


                  • #10
                    Did the AbstractXmlFlowExecutionTests get re-instated

                    I found my way to this thread trying to understand the rationale behind the changing the AbstractXmlFlowExecutionTests from inheriting from AbstractXmlFlowExecutionTests. Some of our developers are quite fond on this style of (integration) testing in Spring apps.

                    I found the info I was looking for, thanks Keith for the informative posts. I understand this is not the recommended practice for unit testing.

                    One of posts indicated this style of testing would be re-instated in rc 1. However, I don't think we see that. Is the facility to auto-rollback available in some form from tests?

                    Personally I like that there is a clean way to avoid any dependency on the service-layer context, but I am not in the majority here...

                    Cheers
                    Drew

                    Comment

                    Working...
                    X