blob: b2c19f9623691fecbe188a73cdfb3b9955043a66 [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML>
<HEAD>
<!-- $PAGETITLE -->
<TITLE>OpenEJB - Functional testing with OpenEJB, Jetty and Selenium</TITLE>
<LINK href="http://openejb.apache.org/all.css" rel="stylesheet" type="text/css">
<!--[if IE]><link rel="stylesheet" type="text/css" media="screen, projection" href="openejb.apache.org/ie.css"><![endif]-->
<LINK rel="SHORTCUT ICON" href="http://openejb.apache.org/images/favicon.ico">
<META http-equiv="Content-Type" content="text/html;charset=UTF-8">
<SCRIPT language="javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js" type="text/javascript"></SCRIPT>
<SCRIPT language="javascript" src="http://openejb.apache.org/tweet/jquery.tweet.js" type="text/javascript"></SCRIPT>
<SCRIPT type="text/javascript">
$(document).ready(function(){
$(".tweet").tweet({
avatar_size: 32,
count: 4,
fetch:25,
username: "openejb",
list: "contributors",
template:"{avatar}{text}",
filter: function(t){ return /openejb/i.test(t["tweet_raw_text"]); },
loading_text: "loading list..."
});
});
</SCRIPT>
</HEAD>
<BODY>
<!-- Delay the loading of the external javascript file needed for labels (as it takes too long to load and visibly holds loading of the page body) -->
<!-- To do this without javascript errors over undefined functions, we need to declare stubs here (that are overrided later by the proper implementations) -->
<SCRIPT language="JavaScript" type="text/javascript">
function doAddLabel(hideTextfieldAfterAddParam)
{
// stub
}
function onAddLabel()
{
// stub
}
function showLabelsInput()
{
// stub
}
</SCRIPT>
<A name="top"></A>
<TABLE class="frameTable" cellpadding="0" cellspacing="0" border="0">
<TR class="Row1">
<TD class="Col1"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col2"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col3"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col4"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col5"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
</TR>
<TR class="Row2">
<TD class="Col1"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col2">&nbsp;</TD>
<TD class="Col3" id="breadcrumbs">
<!-- $TOP_NAV_BAR -->
<A href="index.html" title="Index">Home</A> | <A href="news.html" title="News">News</A> | <A href="faq.html" title="FAQ">FAQ</A> | <A href="download.html" title="Download">Download</A> | <A href="mailing-lists.html" title="Mailing Lists">Lists</A> | <A href="http://issues.apache.org/jira/browse/OPENEJB" class="external-link" rel="nofollow">Issues</A>
</TD>
<TD class="Col4"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col5">&nbsp;</TD>
</TR>
<TR class="Row3">
<TD class="Col1"><IMG alt="" class="Row3Img" id="thinLine" src="http://openejb.apache.org/images/line_sm.gif"></TD>
<TD class="Col2"><IMG alt="" class="Row3Img" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col3"><IMG alt="" class="Row3Img" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col4"><IMG alt="" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col5"><IMG alt="" class="Row3Img" src="http://openejb.apache.org/images/dotTrans.gif"></TD>
</TR>
<TR class="Row4">
<TD class="Col1">
<SPAN id="Navigation">
<H3><A name="Navigation-Overview"></A>Overview</H3>
<UL class="alternate" type="square">
<LI><A href="index.html" title="Index">Home</A></LI>
<LI><A href="news.html" title="News">News</A></LI>
<LI><A href="faq.html" title="FAQ">FAQ</A></LI>
<LI><A href="download.html" title="Download">Download</A></LI>
<LI><A href="../OPENEJBx30/index.html" title="Index">Documentation</A></LI>
<LI><A href="examples.html" title="Examples">Examples</A></LI>
<LI><A href="http://cwiki.apache.org/confluence/display/OPENEJB/Lightening%20Demos" class="external-link" rel="nofollow">Lightning Demos</A></LI>
<LI><A href="mailing-lists.html" title="Mailing Lists">Mailing Lists</A></LI>
<LI><A href="source-code.html" title="Source Code">Source Code</A></LI>
<LI><A href="http://blogs.apache.org/openejb" class="external-link" rel="nofollow">Project Blog</A></LI>
</UL>
<H3><A name="Navigation-Servers"></A>Servers</H3>
<UL class="alternate" type="square">
<LI><A href="local-server.html" title="Local Server">Local</A></LI>
<LI><A href="remote-server.html" title="Remote Server">Remote</A></LI>
</UL>
<H3><A name="Navigation-Integrations"></A>Integrations</H3>
<UL class="alternate" type="square">
<LI><A href="../OPENEJBx30/tomcat.html" title="Tomcat">Tomcat</A></LI>
<LI><A href="geronimo.html" title="Geronimo">Geronimo</A></LI>
<LI><A href="webobjects.html" title="WebObjects">WebObjects</A></LI>
</UL>
<H3><A name="Navigation-Community"></A>Community</H3>
<UL class="alternate" type="square">
<LI><A href="team.html" title="Team">Team</A></LI>
<LI><A href="articles.html" title="Articles">Articles</A></LI>
<LI><A href="http://webchat.freenode.net/?channels=openejb" class="external-link" rel="nofollow">IRC</A></LI>
</UL>
<H3><A name="Navigation-RelatedProjects"></A>Related Projects</H3>
<UL class="alternate" type="square">
<LI><A href="http://activemq.apache.org/" class="external-link" rel="nofollow">ActiveMQ</A></LI>
<LI><A href="http://openjpa.apache.org/" class="external-link" rel="nofollow">OpenJPA</A></LI>
<LI><A href="http://cxf.apache.org/" class="external-link" rel="nofollow">CXF</A></LI>
</UL>
<H3><A name="Navigation-Index"></A>Index</H3>
<UL class="alternate" type="square">
<LI><A href="space-index.html" title="Space Index">Site Index</A></LI>
<LI><A href="../OPENEJBx30/space-index.html" title="Space Index">Doc Index</A></LI>
</UL>
<H3>
<A name="Navigation-Feeds"></A>
Feeds
</H3>
<UL class="feeds">
<LI>
<A href="http://cwiki.apache.org/confluence/spaces/rss.action?key=OPENEJB&newPages=false">
<IMG src="http://openejb.apache.org/images/rss.gif"></A>
<A class="feedsText" href="http://cwiki.apache.org/confluence/spaces/rss.action?key=OPENEJB&newPages=false">Site</A>
</LI>
<LI><A href="http://cwiki.apache.org/confluence/spaces/blogrss.action?key=OPENEJB">
<IMG src="http://openejb.apache.org/images/rss.gif"></A>
<A class="feedsText" href="http://cwiki.apache.org/confluence/spaces/blogrss.action?key=OPENEJB">News</A>
</LI>
</UL>
</SPAN>
</TD>
<TD class="Col2">&nbsp;</TD>
<TD class="Col3">
<TABLE id="PageHeader" border="0" width="100%">
<TR>
<TD>
<A href="http://openejb.org/">
<IMG hspace="0" src="http://openejb.apache.org/images/logo_openejb.gif" vspace="0">
</A>
</TD>
<TD align="right">
<A href="http://www.apache.org/">
<IMG src="http://www.apache.org/images/asf-logo.gif" width="258" height="66">
</A>
</TD>
</TR>
<TR>
<TD id="page_title">
<!-- $TITLE -->
Functional testing with OpenEJB, Jetty and Selenium
</TD>
<TD align="right">
<BR><BR>
<!-- Google CSE Search Box Begins -->
<FORM id="searchbox_010475492895890475512:_t4iqjrgx90" action="http://www.google.com/cse">
<INPUT type="hidden" name="cx" value="010475492895890475512:_t4iqjrgx90">
<INPUT type="hidden" name="cof" value="FORID:0">
<INPUT name="q" type="text" size="25">
<INPUT type="submit" name="sa" value="Search">
</FORM>
<SCRIPT type="text/javascript" src="http://www.google.com/coop/cse/brand?form=searchbox_010475492895890475512:_t4iqjrgx90"></SCRIPT>
<!-- Google CSE Search Box Ends -->
</TD>
</TR>
</TABLE>
<P>
<!-- $BODY -->
<DIV id="PageContent">
<P>Obviously, OpenEJB is great for unit testing EJBs, but I wondered whether I might also be able to use this embedded functionality to functionally test my application. You can use tools like Selenium, or HtmlUnit to run functional tests as if the user were sat at their browser typing text, and clicking links and buttons. This however means you have to have your app running on your app server, and you need to have consistent test data - otherwise a test might pass on one developers machine, but fail on another. Here's one approach that you could take to completely deploy your webapp within a test, and functionally test it with a tool like Selenium. There's also some sample code demonstrating this, available <A href="http://people.apache.org/~jgallimore/PersonApp.zip" class="external-link" rel="nofollow">here</A>.</P>
<H3><A name="FunctionaltestingwithOpenEJB%2CJettyandSelenium-Creatinganembeddedserver"></A>Creating an embedded server</H3>
<P>I created a class to start my embedded OpenEJB and Jetty instances and configure them to see the EJB and WAR modules of my application:</P>
<DIV class="preformatted panel" style="border-width: 1px;"><DIV class="preformattedContent panelContent">
<PRE>public class EmbeddedServer {
private static EmbeddedServer instance = new EmbeddedServer();
private Server server;
private EmbeddedServer() {
try {
// initialize OpenEJB &amp; add some test data
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, &quot;org.apache.openejb.client.LocalInitialContextFactory&quot;);
InitialContext ic = new InitialContext(properties);
PeopleFacade facade = (PeopleFacade) ic.lookup(&quot;PeopleFacadeEJBRemote&quot;);
new TestFixture(facade).addTestData();
// setup web app
WebAppContext context = new WebAppContext();
context.setWar(computeWarPath());
InitialContext initialContext = setupJndi(context);
// start the server
context.setServletHandler(new EmbeddedServerServletHandler(initialContext));
context.setContextPath(&quot;/&quot;);
server = new Server(9091);
server.addHandler(context);
server.start();
} catch (Exception e) {
e.printStackTrace();
}
}
private InitialContext setupJndi(WebAppContext context) throws NamingException {
// setup local JNDI
InitialContext initialContext = new InitialContext();
WebApp webApp = getWebApp(context);
Collection&lt;EjbRef&gt; refs = webApp.getEjbRef();
for (EjbRef ref : refs) {
String ejbLink = ref.getEjbLink();
// get enterprise bean info
EnterpriseBeanInfo beanInfo = new EJBHelper().getEJBInfo(ejbLink);
if (beanInfo.jndiNames != null &amp;&amp; beanInfo.jndiNames.size() &gt; 0) {
String jndiName = &quot;java:openejb/ejb/&quot; + beanInfo.jndiNames.get(0);
initialContext.bind(&quot;java:comp/env/&quot; + ref.getEjbRefName(), new LinkRef(jndiName));
}
}
return initialContext;
}
private String computeWarPath() {
String currentPath = new File(&quot;.&quot;).getAbsolutePath();
String warPath;
String[] pathParts = currentPath.split(&quot;(\\\\|/)+&quot;);
int webPart = Arrays.asList(pathParts).indexOf(&quot;PersonWEB&quot;);
if (webPart == -1) {
warPath = &quot;PersonWEB/src/main/webapp&quot;;
} else {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i &lt; webPart; i++) {
buffer.append(pathParts[i]);
buffer.append(File.separator);
}
buffer.append(&quot;PersonWEB/src/main/webapp&quot;);
warPath = buffer.toString();
}
return warPath;
}
public static EmbeddedServer getInstance() {
return instance;
}
public Server getServer() {
return server;
}
public static void main(String[] args) {
try {
EmbeddedServer.getInstance().getServer().join();
} catch (Exception e) {
e.printStackTrace();
}
}
private WebApp getWebApp(WebAppContext context) {
WebApp webApp = null;
try {
FileInputStream is = new FileInputStream(new File(context.getWar() + &quot;/WEB-INF/web.xml&quot;).getAbsolutePath());
webApp = (WebApp) JaxbJavaee.unmarshal(WebApp.class, is);
} catch (Exception e) {
e.printStackTrace();
}
return webApp;
}
}
</PRE>
</DIV></DIV>
<P>This class sets up an embedded instance of Jetty, running on port 9091. You'll notice the setupJndi() method. This looks through the ejb-ref entries in web.xml (which we deserialize using the openejb-jee library), and adds relevant LinkRefs to the JNDI tree, so you can lookup beans using the java:comp/env/bean format. I've added a main() method here for convenience, so you can run this straight from an IDE, and record tests using tools like the Selenium Firefox plugin.</P>
<H3><A name="FunctionaltestingwithOpenEJB%2CJettyandSelenium-Supporting@EJBDependencyinjection"></A>Supporting @EJB Dependency injection</H3>
<P>In the last code sample, we also set up a custom ServletHandler in Jetty - this is to perform dependency injection. The custom ServletHandler looks like this:</P>
<DIV class="preformatted panel" style="border-width: 1px;"><DIV class="preformattedContent panelContent">
<PRE>public class EmbeddedServerServletHandler extends ServletHandler {
private InitialContext initialContext;
public EmbeddedServerServletHandler(InitialContext initialContext) {
this.initialContext = initialContext;
}
public Servlet customizeServlet(Servlet servlet) throws Exception {
Class&lt;? extends Servlet&gt; servletClass = servlet.getClass();
Field[] declaredFields = servletClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Annotation[] annotations = declaredField.getAnnotations();
for (Annotation annotation : annotations) {
if (EJB.class.equals(annotation.annotationType())) {
// inject into this field
Class&lt;?&gt; fieldType = declaredField.getType();
EnterpriseBeanInfo beanInfo = getBeanFor(fieldType);
if (beanInfo == null) {
continue;
}
String jndiName = &quot;java:openejb/ejb/&quot; + beanInfo.jndiNames.get(0);
Object o = initialContext.lookup(jndiName);
declaredField.setAccessible(true);
declaredField.set(servlet, o);
}
}
}
return super.customizeServlet(servlet);
}
private EnterpriseBeanInfo getBeanFor(Class&lt;?&gt; fieldType) {
return new EJBHelper().getBeanInfo(fieldType);
}
}
</PRE>
</DIV></DIV>
<P>This looks up deployed beans that match the field type, and uses reflection to set the field.</P>
<H3><A name="FunctionaltestingwithOpenEJB%2CJettyandSelenium-WritingaFunctionaltest"></A>Writing a Functional test</H3>
<P>We can now write a functional test. I use a base abstract class to make sure the Embedded server is running, and start Selenium:</P>
<DIV class="preformatted panel" style="border-width: 1px;"><DIV class="preformattedContent panelContent">
<PRE>public abstract class FunctionalTestCase extends TestCase {
protected DefaultSelenium selenium;
protected void setUp() throws Exception {
super.setUp();
EmbeddedServer.getInstance();
selenium = new DefaultSelenium(&quot;localhost&quot;, 4444, &quot;*iexplore&quot;, &quot;http://localhost:9091/&quot;);
selenium.start();
}
protected void tearDown() throws Exception {
selenium.stop();
}
}
</PRE>
</DIV></DIV>
<P>and I can then I write a test like this:</P>
<DIV class="preformatted panel" style="border-width: 1px;"><DIV class="preformattedContent panelContent">
<PRE>public class AddPersonTest extends FunctionalTestCase {
public void testShouldAddAPerson() throws Exception {
selenium.open(&quot;/People&quot;);
selenium.type(&quot;firstname&quot;, &quot;Jonathan&quot;);
selenium.type(&quot;lastname&quot;, &quot;Gallimore&quot;);
selenium.click(&quot;//input[@name='add' and @value='Add']&quot;);
selenium.waitForPageToLoad(&quot;30000&quot;);
selenium.type(&quot;filter&quot;, &quot;gallimore&quot;);
selenium.click(&quot;submit&quot;);
selenium.waitForPageToLoad(&quot;30000&quot;);
assertEquals(1, selenium.getXpathCount(&quot;//div[@id='people']/ul/li&quot;).intValue());
assertEquals(&quot;Jonathan Gallimore&quot;, selenium.getText(&quot;//div[@id='people']/ul/li[1]&quot;));
}
}
</PRE>
</DIV></DIV>
<H3><A name="FunctionaltestingwithOpenEJB%2CJettyandSelenium-Samplecode"></A>Sample code</H3>
<P>I've made a sample project which demonstrates this, source is available <A href="http://people.apache.org/~jgallimore/PersonApp.zip" class="external-link" rel="nofollow">here</A>. You'll need Maven to build it, and you can build it and run the tests by running 'mvn clean install'. If want to run the tests from your IDE, you'll need to have a Selenium server running, which you can do by running 'mvn selenium:start-server'. </P>
</DIV>
</P>
</TD>
<TD class="Col4"><IMG src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col5">
</TD>
</TR>
<TR class="Row5">
<TD class="Col1">&nbsp;</TD>
<TD class="Col2">&nbsp;</TD>
<TD class="Col3">
<BR>
<BR>
<IMG width="100%" height="1" src="http://openejb.apache.org/images/line_light.gif">
<TABLE width="100%">
<TR>
<TD>
<SPAN class="bodyGrey">
<SMALL>
<NOTICE><!-- $FOOTER -->
Apache OpenEJB is an project of The Apache Software Foundation (ASF)
</NOTICE>
<BR>
Site Powered by
<A href="http://atlassian.com/">Atlassian</A>
<A href="http://atlassian.com/confluence/">Confluence</A>
.
</SMALL>
</SPAN>
</TD>
<TD align="right">
<A style="color:#999;font-size:small;font-weight:normal;" href="https://cwiki.apache.org/confluence/pages/editpage.action?spaceKey=OPENEJB&title=Functional%20testing%20with%20OpenEJB,%20Jetty%20and%20Selenium">[ edit ]</A>
</TD>
</TR>
</TABLE>
<BR>
</TD>
<TD class="Col4"><IMG src="http://openejb.apache.org/images/dotTrans.gif"></TD>
<TD class="Col5">&nbsp;</TD>
</TR>
</TABLE>
<!-- Needed for composition plugin -->
<!-- delay the loading of large javascript files to the end so that they don't interfere with the loading of page content -->
<SPAN style="display: none">
<SCRIPT type="text/javascript" language="JavaScript" src="http://cwiki.apache.org/confluence/labels-javascript"></SCRIPT>
<SCRIPT src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</SCRIPT>
<SCRIPT type="text/javascript">
_uacct = "UA-2717626-1";
urchinTracker();
</SCRIPT>
</SPAN>
</BODY>
</HTML>