added throws IOException to View
added test support for asserting response written
This commit is contained in:
@@ -73,7 +73,7 @@ public class JsfView implements View {
|
||||
/**
|
||||
* This implementation performs the standard duties of the JSF RENDER_RESPONSE phase.
|
||||
*/
|
||||
public void render() {
|
||||
public void render() throws IOException {
|
||||
FacesContext facesContext = createFlowFacesContext();
|
||||
facesContext.setViewRoot(viewRoot);
|
||||
facesContext.renderResponse();
|
||||
|
||||
@@ -81,7 +81,7 @@ public class JsfViewTests extends TestCase {
|
||||
jsfMock.tearDown();
|
||||
}
|
||||
|
||||
public final void testRender() {
|
||||
public final void testRender() throws IOException {
|
||||
|
||||
EasyMock.expect(requestContext.getExternalContext()).andStubReturn(new MockExternalContext());
|
||||
EasyMock.expect(requestContext.getFlashScope()).andStubReturn(flashMap);
|
||||
@@ -99,7 +99,7 @@ public class JsfViewTests extends TestCase {
|
||||
assertNull("The FacesContext was not released", FacesContext.getCurrentInstance());
|
||||
}
|
||||
|
||||
public final void testRenderException() {
|
||||
public final void testRenderException() throws IOException {
|
||||
|
||||
EasyMock.expect(requestContext.getExternalContext()).andStubReturn(new MockExternalContext());
|
||||
EasyMock.expect(requestContext.getFlashScope()).andStubReturn(flashMap);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.webflow.engine;
|
||||
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.View;
|
||||
|
||||
/**
|
||||
* Thrown if a IO exception was thrown during view rendering.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class ViewRenderingException extends FlowExecutionException {
|
||||
|
||||
/**
|
||||
* Create a new action execution exception.
|
||||
* @param flowId the current flow
|
||||
* @param stateId the current state (may be null)
|
||||
* @param view the view that generated an unrecoverable exception
|
||||
* @param cause the underlying cause
|
||||
*/
|
||||
public ViewRenderingException(String flowId, String stateId, View view, Throwable cause) {
|
||||
super(flowId, stateId, "Exception thrown rendering " + view + " in state '" + stateId + "' of flow '" + flowId
|
||||
+ "'", cause);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.webflow.engine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
@@ -214,14 +215,18 @@ public class ViewState extends TransitionableState {
|
||||
}
|
||||
}
|
||||
|
||||
private void render(RequestControlContext context, View view) {
|
||||
private void render(RequestControlContext context, View view) throws ViewRenderingException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Rendering + " + view);
|
||||
logger.debug(" Flash scope = " + context.getFlashScope());
|
||||
logger.debug(" Messages = " + context.getMessageContext());
|
||||
}
|
||||
renderActionList.execute(context);
|
||||
view.render();
|
||||
try {
|
||||
view.render();
|
||||
} catch (IOException e) {
|
||||
throw new ViewRenderingException(getOwner().getId(), getId(), view, e);
|
||||
}
|
||||
context.getMessageContext().clearMessages();
|
||||
context.getFlashScope().clear();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.webflow.execution;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Allows the client to participate in flow execution. Encapsulates behavior to send the client an appropriate response
|
||||
* and handle the resulting event once the client responds.
|
||||
@@ -31,8 +33,9 @@ public interface View {
|
||||
|
||||
/**
|
||||
* Render this view's content.
|
||||
* @throws IOException if an IO Exception occured rendering the view
|
||||
*/
|
||||
public void render();
|
||||
public void render() throws IOException;
|
||||
|
||||
/**
|
||||
* Was a user event signaled on this view in this request?
|
||||
|
||||
@@ -256,6 +256,15 @@ public class MockExternalContext implements ExternalContext {
|
||||
getMockRequestParameterMap().put(parameterName, parameterValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id of the event that should be signaled by this context. For use when resuming a flow. This method
|
||||
* depends on a MockViewFactory being configured for parsing the event id on a resume operation.
|
||||
* @param eventId the id of the event to signal
|
||||
*/
|
||||
public void setEventId(String eventId) {
|
||||
putRequestParameter("_eventId", eventId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this request is an ajax request.
|
||||
* @param ajaxRequest true or false
|
||||
@@ -264,6 +273,14 @@ public class MockExternalContext implements ExternalContext {
|
||||
this.ajaxRequest = ajaxRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the implementation of this mock context's response writer.
|
||||
* @return the underlying string writer to use for asserting a specific response was written
|
||||
*/
|
||||
public StringWriter getMockResponseWriter() {
|
||||
return responseWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flag indicating if a flow execution redirect response has been requested by the flow.
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.webflow.test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.webflow.action.ViewFactoryActionAdapter;
|
||||
@@ -102,8 +104,8 @@ class MockViewFactoryCreator implements ViewFactoryCreator {
|
||||
return new Event(this, context.getRequestParameters().get("_eventId"));
|
||||
}
|
||||
|
||||
public void render() {
|
||||
// nothing to do
|
||||
public void render() throws IOException {
|
||||
context.getExternalContext().getResponseWriter().append(viewId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import org.springframework.webflow.execution.FlowExecution;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.FlowExecutionFactory;
|
||||
import org.springframework.webflow.test.MockExternalContext;
|
||||
import org.springframework.webflow.test.MockParameterMap;
|
||||
|
||||
/**
|
||||
* Base class for integration tests that verify a flow executes as expected. Flow execution tests captured by subclasses
|
||||
@@ -119,45 +118,6 @@ public abstract class AbstractFlowExecutionTests extends TestCase {
|
||||
flowExecution.resume(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal the event against the paused flow execution. The event id will be translated into a Event object by the
|
||||
* configured view factory and raised as an Event against the current view state. The event will cause the flow to
|
||||
* change states if it matches a transition.
|
||||
* @param eventId the event identifier
|
||||
*/
|
||||
protected void signalEvent(String eventId) {
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.putRequestParameter("_eventId", eventId);
|
||||
resumeFlow(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal the event against the paused flow execution. The event id will be translated into a Event object by the
|
||||
* configured view factory and raised as an Event against the current view state. The event will cause the flow to
|
||||
* change states if it matches a transition.
|
||||
* @param eventId the event identifier
|
||||
* @param input event input parameters
|
||||
*/
|
||||
protected void signalEvent(String eventId, MockParameterMap input) {
|
||||
MockExternalContext context = new MockExternalContext(input);
|
||||
context.putRequestParameter("_eventId", eventId);
|
||||
resumeFlow(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal the event against the paused flow execution. The event id will be translated into a Event object by the
|
||||
* configured view factory and raised as an Event against the current view state. The event will cause the flow to
|
||||
* change states if it matches a transition.
|
||||
* @param eventId the event identifier
|
||||
* @param parameterName the name of the parameter
|
||||
* @param parameterValue the value of the parameter
|
||||
*/
|
||||
protected void signalEvent(String eventId, String parameterName, String parameterValue) {
|
||||
MockParameterMap input = new MockParameterMap();
|
||||
input.put(parameterName, parameterValue);
|
||||
signalEvent(eventId, input);
|
||||
}
|
||||
|
||||
// convenience accessors
|
||||
|
||||
/**
|
||||
@@ -268,6 +228,13 @@ public abstract class AbstractFlowExecutionTests extends TestCase {
|
||||
|
||||
// assert helpers
|
||||
|
||||
/**
|
||||
* Assert that the entire flow execution is active; that is, it has not ended and has been started.
|
||||
*/
|
||||
protected void assertFlowExecutionActive() {
|
||||
assertTrue("The flow execution is not active but it should be", getFlowExecution().isActive());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the active flow session is for the flow with the provided id.
|
||||
* @param expectedActiveFlowId the flow id that should have a session active in the tested flow execution
|
||||
@@ -278,13 +245,6 @@ public abstract class AbstractFlowExecutionTests extends TestCase {
|
||||
getFlowExecution().getActiveSession().getDefinition().getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the entire flow execution is active; that is, it has not ended and has been started.
|
||||
*/
|
||||
protected void assertFlowExecutionActive() {
|
||||
assertTrue("The flow execution is not active but it should be", getFlowExecution().isActive());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the entire flow execution has ended; that is, it is no longer active.
|
||||
*/
|
||||
@@ -303,6 +263,15 @@ public abstract class AbstractFlowExecutionTests extends TestCase {
|
||||
getFlowExecution().getActiveSession().getState().getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the response written to the mock context equals the response provided.
|
||||
* @param response the expected response
|
||||
* @param context the mock external context that was written to
|
||||
*/
|
||||
protected void assertResponseWrittenEquals(String response, MockExternalContext context) {
|
||||
assertEquals(response, context.getMockResponseWriter().getBuffer().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create the flow execution factory. Subclasses could override this if they want to use a custom
|
||||
* flow execution factory or custom configuration of the flow execution factory, registering flow execution
|
||||
|
||||
@@ -38,7 +38,7 @@ public class MvcViewFactoryTests extends TestCase {
|
||||
context = new StaticApplicationContext();
|
||||
}
|
||||
|
||||
public void testNoResolversGetResource() {
|
||||
public void testNoResolversGetResource() throws Exception {
|
||||
creator.setApplicationContext(context);
|
||||
ResourceLoader viewResourceLoader = new ResourceLoader() {
|
||||
public ClassLoader getClassLoader() {
|
||||
@@ -65,7 +65,7 @@ public class MvcViewFactoryTests extends TestCase {
|
||||
assertEquals("/parent/myview.jsp", response.getForwardedUrl());
|
||||
}
|
||||
|
||||
public void testViewResolversGetResource() {
|
||||
public void testViewResolversGetResource() throws Exception {
|
||||
MockViewResolver viewResolver = new MockViewResolver("myview");
|
||||
creator.setApplicationContext(context);
|
||||
creator.setViewResolvers(Collections.singletonList(viewResolver));
|
||||
@@ -85,7 +85,7 @@ public class MvcViewFactoryTests extends TestCase {
|
||||
assertEquals("myview", response.getForwardedUrl());
|
||||
}
|
||||
|
||||
public void testRestoreView() {
|
||||
public void testRestoreView() throws Exception {
|
||||
creator.setApplicationContext(context);
|
||||
ResourceLoader viewResourceLoader = new ResourceLoader() {
|
||||
public ClassLoader getClassLoader() {
|
||||
@@ -116,7 +116,7 @@ public class MvcViewFactoryTests extends TestCase {
|
||||
assertEquals("/parent/myview.jsp", response.getForwardedUrl());
|
||||
}
|
||||
|
||||
public void testRestoreViewButtonEventIdFormat() {
|
||||
public void testRestoreViewButtonEventIdFormat() throws Exception {
|
||||
creator.setApplicationContext(context);
|
||||
ResourceLoader viewResourceLoader = new ResourceLoader() {
|
||||
public ClassLoader getClassLoader() {
|
||||
|
||||
@@ -45,25 +45,44 @@ public class SearchFlowExecutionTests extends AbstractXmlFlowExecutionTests {
|
||||
|
||||
public void testCriteriaSubmitSuccess() {
|
||||
startFlow(new MockExternalContext());
|
||||
MockParameterMap input = new MockParameterMap();
|
||||
input.put("firstName", "Keith");
|
||||
input.put("lastName", "Donald");
|
||||
signalEvent("search", input);
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.putRequestParameter("firstName", "Keith");
|
||||
context.putRequestParameter("lastName", "Donald");
|
||||
context.setEventId("search");
|
||||
resumeFlow(context);
|
||||
assertCurrentStateEquals("displayResults");
|
||||
assertResponseWrittenEquals("searchResults", context);
|
||||
}
|
||||
|
||||
public void testNewSearch() {
|
||||
startFlow(new MockExternalContext());
|
||||
signalEvent("search");
|
||||
signalEvent("newSearch");
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.putRequestParameter("firstName", "Keith");
|
||||
context.putRequestParameter("lastName", "Donald");
|
||||
context.setEventId("search");
|
||||
resumeFlow(context);
|
||||
|
||||
context = new MockExternalContext();
|
||||
context.setEventId("newSearch");
|
||||
resumeFlow(context);
|
||||
assertCurrentStateEquals("enterCriteria");
|
||||
assertResponseWrittenEquals("searchCriteria", context);
|
||||
}
|
||||
|
||||
public void testSelectValidResult() {
|
||||
startFlow(new MockExternalContext());
|
||||
signalEvent("search");
|
||||
signalEvent("select", "id", "1");
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.putRequestParameter("firstName", "Keith");
|
||||
context.putRequestParameter("lastName", "Donald");
|
||||
context.setEventId("search");
|
||||
resumeFlow(context);
|
||||
|
||||
context = new MockExternalContext();
|
||||
context.setEventId("select");
|
||||
context.putRequestParameter("id", "1");
|
||||
resumeFlow(context);
|
||||
assertCurrentStateEquals("displayResults");
|
||||
assertResponseWrittenEquals("searchResults", context);
|
||||
}
|
||||
|
||||
protected void configureFlowBuilderContext(MockFlowBuilderContext builderContext) {
|
||||
|
||||
Reference in New Issue
Block a user