Raible's Wiki

Raible Designs
Wiki Home
News
Recent Changes

AppFuse

Homepage
  - Korean
  - Chinese
  - Italian
  - Japanese

QuickStart Guide
  - Chinese
  - French
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish
  - Japanese

User Guide
  - Korean
  - Chinese

Tutorials
  - Chinese
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish

FAQ
  - Korean

Latest Downloads

Other Applications

Struts Resume
Security Example
Struts Menu

Set your name in
UserPreferences


Referenced by
Articles
Articles_cn
Articles_pt
Articles_zh
CreateActions
CreateActions_pt
CreateActions_zh
SpringControllerUnit...
SpringControllers_ko
ValidationAndListSpr...
...and 1 more




JSPWiki v2.2.33

[RSS]


Hide Menu

SpringControllers


Difference between version 53 and version 2:

At line 1 changed 1 line.
__Part III:__ [Creating Controllers and JSPs|SpringControllers] - A HowTo for creating [Spring|http://www.springframework.org] Controllers and JSPs in the [AppFuse] architecture.
__Part III:__ [Creating Controllers and JSPs|SpringControllers] - A HowTo for creating [Spring|http://www.springframework.org] Controllers and JSPs in an [AppFuse] project.
At line 6 changed 1 line.
This tutorial will show you how to create two Spring Controllers - one for a list screen, and one for the form. It'll also demonstrate writing a JUnit Test to test the Controllers, and two JSPs. The Controller we create will talk to the PersonManager we created in the [Creating Managers|CreateManager] tutorial. This tutorial will simplify everything - we will not actually be rendering any data or making the UI look pretty. The [next tutorial|ConfiguringTiles] will show you how to integrate your new JSPs into your webapp.
This tutorial will show you how to create Spring Controllers and JSPs. It'll also demonstrate writing JUnit Tests to test your Controllers. The Controller we create will talk to the PersonManager we created in the [Creating Managers|CreateManager] tutorial.
At line 10 changed 1 line.
Let's get started by creating a new Controller and JSP in AppFuse's architecture. If you haven't installed the Spring MVC module at this point, do so by running "ant install-springmvc".
Let's get started by creating a new Controller and JSP in AppFuse's architecture. If you haven't installed the Spring MVC module at this point, do so by running ''ant install-springmvc''.
At line 13 changed 4 lines.
* [1] Create a skeleton JSP using XDoclet
* [2] Create a new ControllerTest to test our Controller
* [3] Create a new Controller
* [4] Display the JSP in a browser and run the ActionTest
* [1] Create skeleton JSPs using XDoclet
* [2] Create PersonFormControllerTest to test PersonFormController
* [3] Create PersonFormController
* [4] Run PersonFormControllerTest
* [5] Clean up the JSP to make it presentable
* [6] Create Canoo WebTests to test browser-like actions
At line 18 changed 2 lines.
!!Create a skeleton JSP using XDoclet [#2]
In this step, we'll generate a ''skeleton'' or our JSP for displaying information from the PersonForm. I say ''skeleton'' because it'll just be the <form> itself. It will contain table rows and Struts' <html:text> tags for each property in PersonForm.java. The tool that we use to do this was written by [Erik Hatcher|http://www.blogscene.org/erik/]. It's basically just a single class (FormTagsHandler.java) and a couple of XDoclet templates (FormKeys.xdt and StrutsForm_jsp.xdt). All these files are located in extras/jspgen.
!!Create a skeleton JSP using XDoclet [#1]
In this step, you'll generate a JSP page to display information from the Person object. It will contain Spring's JSP tags that render table rows for each property in Person.java. The [AppGen] tool that's used to do this is based off a StrutsGen tool - which was originally written by [Erik Hatcher|http://www.blogscene.org/erik/]. It's basically just a couple of classes and a bunch of XDoclet templates. All these files are located in extras/appgen.
At line 23 changed 7 lines.
* Execute __ant webdoclet__ - this generates the PersonForm.java from the Person.java POJO.
* From the command-line, navigate to "extras/jspgen"
* Execute __ant -Dform.name=PersonForm__ to generate three files in extras/jspgen/build:
** PersonForm.properties (labels for your form elements)
** personForm.jsp (skeleton JSP file for viewing a single Person)
** PersonFormList.jsp (skeleton JSP file for viewing a list of People)
* Copy the contents of PersonForm.properties into web/WEB-INF/classes/ApplicationResources.properties. Here is an example of what you might add to ApplicationResources.properties:
* From the command-line, navigate to "extras/appgen"
* Execute __ant -Dobject.name=Person -Dappgen.type=pojo -Dapp.module=__ to generate a bunch of files in extras/appgen/build/gen. In fact, it'll generate all the files you need to complete this tutorial. However, let's just grab the ones you need.
** web/WEB-INF/classes/Person.properties (labels for your form elements)
** web/pages/personForm.jsp (JSP file for viewing a single Person)
** web/pages/personList.jsp (JSP file for viewing a list of People)
* Copy the contents of Person.properties into web/WEB-INF/classes/ApplicationResources.properties. These are all the keys you will need for titles/headings and form properties. Here is an example of what you should add to ApplicationResources.properties:
At line 33 changed 3 lines.
personForm.firstName=First Name
personForm.id=Id
personForm.lastName=Last Name
person.id=Id
person.firstName=First Name
person.lastName=Last Name
person.added=Person has been added successfully.
person.updated=Person has been updated successfully.
person.deleted=Person has been deleted successfully.
# -- person list page --
personList.title=Person List
personList.heading=Persons
# -- person detail page --
personDetail.title=Person Detail
personDetail.heading=Person Information
At line 38 changed 1 line.
* Copy personForm.jsp to web/personForm.jsp. Copy PersonFormList.jsp to web/personList.jsp. ''Notice that each of the new filename's first character is lowercase.''
* Copy personForm.jsp to web/pages/personForm.jsp. Copy personList.jsp to web/pages/personList.jsp.
At line 40 changed 1 line.
We copy the JSPs to the ''web'' folder instead of ''web/pages'' because the ''pages'' directory ends up in ''WEB-INF/pages'' when the application is packaged into a [WAR|http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/WCC3.html#69310] file. This is a [recommended practice|http://husted.com/struts/catalog.html] when building secure web applications.
;: ''The files in the "pages" directory will end up in "WEB-INF/pages" at deployment time. The container provides security for all files below WEB-INF. This applies to client requests, but not to forwards from the DispatchServlet. Placing all JSPs below WEB-INF ensures they are only accessed through Controllers, and not directly by the client or each other. This allows security to be moved up into the Controller, where it can be handled more efficiently, and out of the base presentation layer.''
At line 42 changed 1 line.
;: ''The container provides security for all files below WEB-INF. This applies to client requests, but not to forwards from the ActionServlet. Placing all JSPs below WEB-INF ensures they are only accessed through Actions, and not directly by the client or each other. This allows security to be moved up into the Controller, where it can be handled more efficiently, and out of the base presentation layer.''
The web application security for AppFuse specifies that all *.html url-patterns should be protected (except for /signup.html and /passwordHint.html). This guarantees that clients must go through an Action to get to a JSP (or at least the ones in ''pages'').
At line 44 changed 1 line.
The web application security for AppFuse specifies that all *.html url-patterns should be protected. This guarantees 1) all Actions are protected, and 2) you must go through an Action to get to a JSP (or at least the ones in ''pages'').
%%note __NOTE:__ If you want to customize the CSS for a particular page, you can add <body id="pageName"/> to the top of the file. This will be slurped up by SiteMesh and put into the final page. You can then customize your CSS on a page-by-page basis using something like the following:
{{{body#pageName element.class { background-color: blue } }}}%%
At line 46 changed 1 line.
All this is to say that __putting the personForm.jsp in the ''web'' folder will allow us to view it without making a Tile for it__. ''We'll get to that in the next tutorial.''
* Add keys in ApplicationResources.properties the titles and headings in the JSPs
In the generated JSPs, there are two keys for the title (top of the browser window) and the header (heading in the page). We now need to add these two keys (personDetail.title and personDetail.heading) to ApplicationResources.properties.
At line 48 removed 3 lines.
At this point, you won't be able to view the JSP in your browser because the <html:form> tag in personForm.jsp has ''action="savePerson"'' - and this action-mapping doesn't exist (yet) in struts-config.xml. You can try it yourself (cd ../.. first) by setting up AppFuse on Tomcat using __ant setup-db setup-tomcat deploy__.
Then, start Tomcat and then go to [http://localhost:8080/appfuse/personForm.jsp]. This will result in the following error:
At line 52 changed 1 line.
javax.servlet.jsp.JspException: Cannot retrieve mapping for action /savePerson
# -- person detail page --
personDetail.title=Person Detail
personDetail.heading=Person Information
At line 54 removed 1 line.
Therefore, we need to create an Action for this JSP, and we should probably create a Test before we write our Action.
At line 56 changed 2 lines.
!!Create a new ActionTest to test our Action [#3]
To create a StrutsTestCase Test for our Action, start by creating a PersonActionTest.java file in the test/web/**/action directory.
;:''Just above, we added "personForm.*" keys to this file, so why do I use personForm ''and'' personDetail? The best reason is because it gives a nice separation between form labels and text on the page. Another reason is because all the *Form.* give you a nice representation of all the fields in your database.
At line 59 changed 1 line.
;:%%(color: blue)''As usual, copy → save as an existing ActionTest (i.e. UserActionTest). Replace [[Uu]ser with [[P]erson. You might want to make sure [Cactus|http://jakarta.apache.org/cactus] (StrutsTestCase is an extension of Cactus] tests are running before you copy an existing one. Run __ant test-cactus -Dtestcase=UserAction__ to verify the UserAction works. Stop Tomcat before you do this.''%%
I recently had a client who wanted all fields in the database searchable. This was fairly easy to do. I just looked up all the keys in ApplicationResources.properties which contained "Form." and then put them into a drop-down. On the UI, the user was able to enter a search term and select the column they wanted to search. I was glad I followed this Form vs. Detail distinction on that project!''
At line 61 removed 1 line.
If you did copy UserActionTest, make sure and change ''UserFormEx'' to ''PersonForm''. The reason for ''UserFormEx'' is to support indexed properties and non-struts validation. Since the UserForm is generated, it's not very feasible to do it in the User.java object.
At line 63 changed 1 line.
When we do create an Action (in [step 4|4]), we're only going to create an __execute__ method, rather than all the different CRUD methods. So let's just test that method to start.
!!Create PersonFormControllerTest to test PersonFormController [#2]
To create a JUnit Test for the PersonFormController, start by creating a PersonFormControllerTest.java file in the test/web/**/action directory.
At line 69 changed 4 lines.
public class PersonActionTest extends BaseStrutsTestCase {
public PersonActionTest(String name) {
super(name);
import org.appfuse.model.Person;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.servlet.ModelAndView;
public class PersonFormControllerTest extends BaseControllerTestCase {
private PersonFormController c;
private MockHttpServletRequest request;
private ModelAndView mv;
protected void setUp() throws Exception {
// needed to initialize a user
super.setUp();
c = (PersonFormController) ctx.getBean("personFormController");
At line 75 changed 6 lines.
public void testExecute() {
// test execute method
setRequestPathInfo("/editPerson");
addRequestParameter("id", "1");
actionPerform();
verifyNoActionErrors();
protected void tearDown() {
c = null;
At line 102 added 39 lines.
public void testEdit() throws Exception {
log.debug("testing edit...");
request = newGet("/editPerson.html");
request.addParameter("username", "tomcat");
mv = c.handleRequest(request, new MockHttpServletResponse());
assertEquals("personForm", mv.getViewName());
}
public void testSave() throws Exception {
request = newGet("/editPerson.html");
request.addParameter("id", "1");
mv = c.handleRequest(request, new MockHttpServletResponse());
Person person = (Person) mv.getModel().get(c.getCommandName());
assertNotNull(person);
request = newPost("/editPerson.html");
super.objectToRequestParameters(person, request);
request.addParameter("lastName", "Updated Last Name");
mv = c.handleRequest(request, new MockHttpServletResponse());
Errors errors =
(Errors) mv.getModel().get(BindException.ERROR_KEY_PREFIX + "person");
assertNull(errors);
assertNotNull(request.getSession().getAttribute("successMessages"));
}
public void testRemove() throws Exception {
request = newPost("/editPerson.html");
request.addParameter("delete", "");
request.addParameter("id", "2");
mv = c.handleRequest(request, new MockHttpServletResponse());
assertNotNull(request.getSession().getAttribute("successMessages"));
}
At line 85 changed 1 line.
Everything should compile at this point (ant compile-web) since we're not referring to the PersonAction directly in our test. However, if you try to run __ant test-cactus -Dtestcase=PersonAction__, it won't work (make sure Tomcat is ''not'' running if you decide to try this).
Nothing will compile at this point (ant compile) because you need to create the PersonFormController that you're referring to in this test.
At line 87 removed 1 line.
!!Create a new Action [#4]
At line 89 changed 1 line.
Now we have to create an Action (a.k.a. the Controller) to talk to our Manager and retrieve/save our data. In src/web/**/action, create a PersonAction.java file with the following contents:
!!Create PersonFormController [#3]
At line 149 added 2 lines.
In src/web/**/action, create a PersonFormController.java file with the following contents:
At line 155 added 2 lines.
import java.util.Locale;
At line 98 changed 5 lines.
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.commons.lang.StringUtils;
import org.appfuse.model.Person;
import org.appfuse.service.PersonManager;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
At line 167 added 2 lines.
public class PersonFormController extends BaseFormController {
private PersonManager mgr = null;
At line 105 changed 10 lines.
/**
* Implementation of <strong>Action</strong> that interacts with the {@link
* PersonForm} and retrieves values. It interacts with the {@link
* PersonManager} to retrieve/persist values to the database.
*
* @struts.action name="personForm" path="/editPerson" scope="request"
* validate="false" parameter="action" input="mainMenu"
*/
public final class PersonAction extends BaseAction {
private static Log log = LogFactory.getLog(PersonAction.class);
public void setPersonManager(PersonManager mgr) {
this.mgr = mgr;
}
At line 116 changed 3 lines.
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
protected Object formBackingObject(HttpServletRequest request)
At line 176 added 29 lines.
String id = request.getParameter("id");
Person person = null;
if (!StringUtils.isEmpty(id)) {
person = mgr.getPerson(id);
} else {
person = new Person();
}
return person;
}
public ModelAndView processFormSubmission(HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException errors)
throws Exception {
if (request.getParameter("cancel") != null) {
return new ModelAndView(new RedirectView(getSuccessView()));
}
return super.processFormSubmission(request, response, command, errors);
}
public ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response, Object command,
BindException errors)
throws Exception {
At line 121 changed 1 line.
log.debug("Entering 'execute' method");
log.debug("entering 'onSubmit' method...");
At line 124 changed 2 lines.
// return nothing (yet)
return null;
Person person = (Person) command;
boolean isNew = (person.getId() == null);
String success = getSuccessView();
Locale locale = request.getLocale();
if (request.getParameter("delete") != null) {
mgr.removePerson(person.getId().toString());
saveMessage(request, getText("person.deleted", locale));
} else {
mgr.savePerson(person);
String key = (isNew) ? "person.added" : "person.updated";
saveMessage(request, getText(key, locale));
if (!isNew) {
success = "editPerson.html?id=" + person.getId();
}
}
return new ModelAndView(new RedirectView(success));
At line 130 changed 1 line.
We're not putting much in PersonAction at this point because we just want to 1) render the JSP and 2) verify our Test runs. The XDoclet tags (beginning with ''@struts.action'') will generate the following XML in the build/appfuse/WEB-INF/struts-config.xml file (when you run __ant webdoclet__):
In the class above, there are a few methods you might not be familiar with. The {{formBackingObject()}} method is used to supply the object this Controller operates on. The {{processFormSubmission()}} method is used to detect the cancel button, and {{onSubmit()}} is called on POST requests and handles delete/add/update of a user.
At line 236 added 15 lines.
There are a few keys you (might) need to add to ApplicationResources.properties to display the success messages. This file is located in ''web/WEB-INF/classes'' - open it and add the following:
;:''I usually add these under the {{# -- success messages --}} comment.''
{{{person.added=Person has been added successfully.
person.updated=Person has been updated successfully.
person.deleted=Person has been deleted successfully.
}}}
You could use generic ''added'', ''deleted'' and ''updated'' messages, whatever works for you. It's nice to have separate messages in case these need to change on a per-entity basis.
You might notice that the code we're using to call the PersonManager is the same as the code we used in our PersonManagerTest. Both PersonFormController and PersonManagerTest are ''clients'' of PersonManagerImpl, so this makes perfect sense.
Now you need to add a url-mapping for this controller in the web/WEB-INF/action-servlet.xml file. In the block below, the new line is at the bottom, with __&lt;prop key="/editPerson.html"&gt;__:
At line 134 changed 4 lines.
<action path="/editPerson" type="org.appfuse.webapp.action.PersonAction"
name="personForm" scope="request" input="mainMenu"
parameter="action" unknown="false" validate="false">
</action>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/editProfile.html">userFormController</prop>
<prop key="/mainMenu.html">filenameController</prop>
<prop key="/editUser.html">userFormController</prop>
<prop key="/selectFile.html">filenameController</prop>
<prop key="/uploadFile.html">fileUploadController</prop>
<prop key="/passwordHint.xml">passwordHintController</prop>
<prop key="/signup.xml">signupController</prop>
<prop key="/editPerson.html">personFormController</prop>
</props>
</property>
</bean>
At line 140 changed 1 line.
;:''I formatted the XML above the the purposes of the tutorial. No content has changed.''
You also need to add the &lt;bean&gt; definition for personFormController in this same file:
At line 142 removed 2 lines.
Now we need to change the "action" attribute in the &lt;personForm.jsp&gt; to be ''action="editPerson"''.
At line 146 changed 2 lines.
<html:form action="editPerson" method="post" styleId="personForm"
focus="" onsubmit="return validatePersonForm(this)">
<bean id="personFormController" class="org.appfuse.webapp.action.PersonFormController">
<property name="commandName"><value>person</value></property>
<property name="commandClass"><value>org.appfuse.model.Person</value></property>
<!--property name="validator"><ref bean="beanValidator"/></property-->
<property name="formView"><value>personForm</value></property>
<property name="successView"><value>mainMenu.html</value></property>
<property name="personManager"><ref bean="personManager"/></property>
</bean>
At line 150 changed 1 line.
Everything is almost done for this tutorial, let's get to running our tests!
;:''The "validator" property is commented out in the above XML block because we haven't defined any validation rules for the Person object. We'll uncomment this value when we add validation.''
At line 152 changed 1 line.
!!Display the JSP in a browser and run the ActionTest [#5]
!!Run the PersonFormControllerTest [#4]
If you look at our PersonFormControllerTest, all the tests depend on having a record with id=1 in the database (and testRemove depends on id=2), so let's add those records to our sample data file (metadata/sql/sample-data.xml). I'd just add it at the bottom - order is not important since it (currently) does not relate to any other tables.
At line 154 changed 1 line.
To test the JSP visually in your browser, save everything, run __ant deploy__, start Tomcat, and navigate to [http://localhost:8080/appfuse/personForm.jsp]. You should see something similar to the following image in your browser:
{{{
<table name='person'>
<column>id</column>
<column>first_name</column>
<column>last_name</column>
<row>
<value>1</value>
<value>Matt</value>
<value>Raible</value>
</row>
<row>
<value>2</value>
<value>James</value>
<value>Davidson</value>
</row>
</table>
}}}
At line 156 changed 2 lines.
%%(border: 1px solid black; height: 125px; width: 270px; margin: 0 auto;)
[personForm-plain.png]
DBUnit loads this file before we running any of the tests, so this record will be available to your Controller test.
Make sure are in the base directory of your project. If you run __ant test-web -Dtestcase=PersonFormController__ - everything should work as planned.
%%(color:green)BUILD SUCCESSFUL\\
Total time: 21 seconds%%
!!Clean up the JSP to make it presentable [#5]
If you want to add a usability enhancement to your form, you can set the cursor to focus on the first field when the page loads. Simply add the following JavaScript at the bottom of your form:
{{{<script type="text/javascript">
document.forms["person"].elements["firstName"].focus();
</script>}}}
Now if you execute __ant db-load deploy__, start Tomcat and point your browser to [http://localhost:8080/appfuse/editPerson.html?id=1], you should see something like this:
%%(border: 1px solid black; margin: 0 auto; height: 166px; width: 337px)
[CreateActions/personForm-final.png]
At line 160 changed 1 line.
;:''There is also a __deploy-web__ target in build.xml that will allow you to just deploy the files in the web directory. Nothing gets compiled or generated when you use this target.''
Finally, to make this page more user friendly, you may want to add a message for your users at the top of the form, but this can easily be done by adding text (using &lt;fmt:message&gt;) at the top of the personForm.jsp page.
At line 162 changed 1 line.
Now, if you stop Tomcat and run __ant test-cactus -Dtestcase=PersonAction__, that should work too!
!![[Optional] Create a Canoo WebTest to test browser-like actions [#6]
The final (optional) step in this tutorial is to create a [Canoo WebTest|http://webtest.canoo.com] to test the JSPs.
At line 334 added 101 lines.
;:''I say this step is optional, because you can run the same tests through your browser.''
You can use the following URLs to test the different actions for adding, editing and saving a user.
* Add - [http://localhost:8080/appfuse/editPerson.html].
* Edit - [http://localhost:8080/appfuse/editPerson.html?id=1] (make sure and run __ant db-load__ first).
* Delete - [http://localhost:8080/appfuse/editPerson.html?method=Delete&amp;id=1] (or edit and click on the Delete button).
* Save - Click [edit|http://localhost:8080/appfuse/editPerson.html?id=1] and then click the Save button.
Canoo tests are pretty slick in that they're simply configured in an XML file. To add tests for add, edit, save and delete, open test/web/web-tests.xml and add the following XML. You'll notice that this fragment has a target named ''PersonTests'' that runs all the related tests.
;:''I use CamelCase target names (vs. the traditional lowercase, dash-separated) because when you're typing ''-Dtestcase=Name'', I've found that I'm used to doing CamelCase for my JUnit Tests.''
[{Java2HtmlPlugin
<!-- runs person-related tests -->
<target name="PersonTests"
depends="EditPerson,SavePerson,AddPerson,DeletePerson"
description="Call and executes all person test cases (targets)">
<echo>Successfully ran all Person JSP tests!</echo>
</target>
<!-- Verify the edit person screen displays without errors -->
<target name="EditPerson"
description="Tests editing an existing Person's information">
<webtest name="editPerson">
&config;
<steps>
&login;
<invoke description="click Edit Person link" url="/editPerson.html?id=1"/>
<verifytitle description="we should see the personDetail title"
text=".*${personDetail.title}.*" regex="true"/>
</steps>
</webtest>
</target>
<!-- Edit a person and then save -->
<target name="SavePerson"
description="Tests editing and saving a user">
<webtest name="savePerson">
&config;
<steps>
&login;
<invoke description="click Edit Person link" url="/editPerson.html?id=1"/>
<verifytitle description="we should see the personDetail title"
text=".*${personDetail.title}.*" regex="true"/>
<setinputfield description="set lastName" name="lastName" value="Canoo"/>
<clickbutton label="Save" description="Click Save"/>
<verifytitle description="Page re-appears if save successful"
text=".*${personDetail.title}.*" regex="true"/>
<verifytext description="verify success message" text="${person.updated}"/>
</steps>
</webtest>
</target>
<!-- Add a new Person -->
<target name="AddPerson"
description="Adds a new Person">
<webtest name="addPerson">
&config;
<steps>
&login;
<invoke description="click Add Button" url="/editPerson.html"/>
<verifytitle description="we should see the personDetail title"
text=".*${personDetail.title}.*" regex="true"/>
<setinputfield description="set firstName" name="firstName" value="Abbie"/>
<setinputfield description="set lastName" name="lastName" value="Raible"/>
<clickbutton label="${button.save}" description="Click button 'Save'"/>
<verifytitle description="Main Menu appears if save successful"
text=".*${mainMenu.title}.*" regex="true"/>
<verifytext description="verify success message" text="${person.added}"/>
</steps>
</webtest>
</target>
<!-- Delete existing person -->
<target name="DeletePerson"
description="Deletes existing Person">
<webtest name="deletePerson">
&config;
<steps>
&login;
<invoke description="click Edit Person link" url="/editPerson.html?id=1"/>
<prepareDialogResponse description="Confirm delete" dialogType="confirm" response="true"/>
<clickbutton label="${button.delete}" description="Click button 'Delete'"/>
<verifyNoDialogResponses/>
<verifytitle description="display Main Menu" text=".*${mainMenu.title}.*" regex="true"/>
<verifytext description="verify success message" text="${person.deleted}"/>
</steps>
</webtest>
</target>
}]
After adding this, you should be able to run __ant test-canoo -Dtestcase=PersonTests__ with Tomcat running or __ant test-jsp -Dtestcase=PersonTests__ if you want Ant to start/stop Tomcat for you. To include the PersonTests when all Canoo tests are run, add it as a dependency to the "run-all-tests" target.
You'll notice that there's no logging in the client-side window by Canoo. If you'd like to see what it's doing, you can add the following between &lt;/webtest&gt; and &lt;/target&gt; at the end of each target.
{{{<loadfile property="web-tests.result"
srcFile="${test.dir}/data/web-tests-result.xml"/>
<echo>${web-tests.result}</echo>}}}
At line 165 changed 1 line.
Total time: 51 seconds%%
Total time: 10 seconds%%
At line 167 removed 1 line.
;:''Look in your console's log for <span style="color: purple">PersonAction.execute(33) | Entering 'execute' method</span>. This is the log.debug statement we put in our execute method. You should also be able to [view personForm.jsp|http://localhost:8080/appfuse/personForm.jsp] (make sure Tomcat is running and you've logged into AppFuse) and click the "Save" button to see the same debug message.''
At line 171 changed 1 line.
''Next Up:'' __Part IV:__ [Configuring Tiles and Action CRUD methods|ConfiguringTiles] - Integrating personForm.jsp with Tiles, replacing execute with different CRUD methods (add, edit, delete), customizing the JSP so it looks good and finally - writing a WebTest to test the JSPs functionality.
''Next Up:'' __Part IV:__ [Adding Validation and List Screen|ValidationAndListSpring] - Adding validation logic to the personForm so that firstName and lastName are required fields and adding a list screen to display all person records in the database.

Back to SpringControllers, or to the Page History.