Introduction
I was looking for a simple solution to test@WebFilter
servlet filters. The tests that I had used a lot of mocks, but really just left me unsatisfied. It was not a real world production solution. How would they "really" behave in the actual server environment. I had been using Arquillian to do some testing of our CDI layer, and decided to try it on filters.I looked at the API, and examples and found really nothing. I found one really "bogus" test example from core api tests that left me really wondering if I should bother. As many of you know though, I love a challenge. I tried a number of ideas including making the filter a JSR-316
@ManagedBean
. I am not sure what glue I sniffed for that one (it didn't work...), and using @Inject
(that didn't work either... more glue).What I settled on was using Arquillian Warp and Drone to check the
HttpServletRequest
and HttpServletResponse
after the filter processed it. There are a couple of annotations @BeforeServlet
and @AfterServlet
that helped me with checking my filters. There are no before and after filter annotations though. That would have made life easier.
Note: The thing to keep in mind with these annotations is that the filtering will have already occurred. You are looking at the post-filter results.
I also wanted to be able to demonstrate another really cool feature called ShrinkWrap, and its Java EE descriptor extensions. This allows you to programmatically create your
web.xml
file to provide additional flexibility like overriding annotations.
Problem Description
We want to be able to test a@WebFilter
that modifies HttpServletRequest
attributes, and HttpServletResponse
headers. The filter should be tested in isolation, and in a real container to examine the real-world response. The tests should be able to be run on a variety of containers with no modification to the test code.
A Solution
Using Maven, Arquillian, Warp, Drone, ShrinkWrap, and GlassFish 3.1.2.2 embedded we can accomplish all aspects of the testing requirements. I used GlassFish since it is the Reference Implementation for Java EE 5, EE 6 and EE7, and it is a great application server. The container can be swapped out easily in maven with another profile. In the example code below, I used NetBeans 7.4 Beta to do the development.The code for the project can be found on BitBucket here: maven-arqullian-filter-test
There are two types of examples included. One set of examples demonstrate simple integration tests that use annotations, and annotation override using
web.xml
. The second set of examples demonstrate dynamic web.xml
generation, and overriding. Here are some code snippets from the project.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.bluelotussoftware.example.filter; | |
import java.net.URL; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.List; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import org.jboss.arquillian.container.test.api.Deployment; | |
import org.jboss.arquillian.container.test.api.RunAsClient; | |
import org.jboss.arquillian.drone.api.annotation.Drone; | |
import org.jboss.arquillian.junit.Arquillian; | |
import org.jboss.arquillian.test.api.ArquillianResource; | |
import org.jboss.arquillian.warp.Activity; | |
import org.jboss.arquillian.warp.Inspection; | |
import org.jboss.arquillian.warp.Warp; | |
import org.jboss.arquillian.warp.WarpTest; | |
import org.jboss.arquillian.warp.servlet.AfterServlet; | |
import org.jboss.arquillian.warp.servlet.BeforeServlet; | |
import org.jboss.shrinkwrap.api.ShrinkWrap; | |
import org.jboss.shrinkwrap.api.spec.WebArchive; | |
import org.junit.Test; | |
import static org.junit.Assert.*; | |
import org.junit.runner.RunWith; | |
import org.openqa.selenium.WebDriver; | |
/** | |
* <p> | |
* Checks the {@link HeaderFilter} initialization parameter set the enabled to | |
* confirm that headers are added to the {@link HttpServletRequest}, and | |
* {@link HttpServletResponse}.</p> | |
* <p> | |
* This test class deployment represents the simplest test form for deployment | |
* of a {@literal Servlet 3.0} based Filter that takes advantage of | |
* annotations.</p> | |
* | |
* @author John Yeary | |
* @version 1.0 | |
* @see HeaderFilterRegistrationIntegrationTest | |
*/ | |
@RunWith(value = Arquillian.class) | |
@WarpTest | |
@RunAsClient | |
public class HeaderFilterIntegrationTest { | |
@ArquillianResource | |
private URL contextPath; | |
@Drone | |
private WebDriver webdriver; | |
/** | |
* Default constructor. | |
*/ | |
public HeaderFilterIntegrationTest() { | |
} | |
/** | |
* Creates a {@link WebArchive} for auto-deployment in Arquillian. | |
* | |
* @return an Web ARchive (war) file representing the filter to be tested. | |
*/ | |
@Deployment | |
public static WebArchive createDeployment() { | |
WebArchive war = ShrinkWrap.create(WebArchive.class) | |
.addClass(HeaderFilter.class); | |
System.out.println(war.toString(true)); | |
return war; | |
} | |
/** | |
* Executes the doFilter() method of the filter and checks the expected | |
* results. | |
*/ | |
@Test | |
public void testDoFilter() { | |
Warp.initiate(new Activity() { | |
@Override | |
public void perform() { | |
System.out.println("TEST URL: " + contextPath.toString()); | |
webdriver.navigate().to(contextPath); | |
} | |
}).inspect(new Inspection() { | |
private static final long serialVersionUID = 1489207995240215531L; | |
@ArquillianResource | |
HttpServletRequest request; | |
@ArquillianResource | |
HttpServletResponse response; | |
@BeforeServlet | |
public void before() { | |
List attributeNames = Collections.list(request.getAttributeNames()); | |
System.out.println("Attributes: " + Arrays.toString(attributeNames.toArray())); | |
String attribute = (String) request.getAttribute("X-ATTRIBUTE"); | |
assertEquals("Experimental Attribute", attribute); | |
} | |
@AfterServlet | |
public void after() { | |
System.out.println("Headers: " + Arrays.toString(response.getHeaderNames().toArray())); | |
assertEquals("Experimental Header", response.getHeader("X-HEADER")); | |
} | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.bluelotussoftware.example.filter; | |
import java.net.URL; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.List; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import org.jboss.arquillian.container.test.api.Deployment; | |
import org.jboss.arquillian.container.test.api.RunAsClient; | |
import org.jboss.arquillian.drone.api.annotation.Drone; | |
import org.jboss.arquillian.junit.Arquillian; | |
import org.jboss.arquillian.test.api.ArquillianResource; | |
import org.jboss.arquillian.warp.Activity; | |
import org.jboss.arquillian.warp.Inspection; | |
import org.jboss.arquillian.warp.Warp; | |
import org.jboss.arquillian.warp.WarpTest; | |
import org.jboss.arquillian.warp.servlet.AfterServlet; | |
import org.jboss.arquillian.warp.servlet.BeforeServlet; | |
import org.jboss.shrinkwrap.api.ShrinkWrap; | |
import org.jboss.shrinkwrap.api.asset.Asset; | |
import org.jboss.shrinkwrap.api.asset.StringAsset; | |
import org.jboss.shrinkwrap.api.spec.WebArchive; | |
import org.jboss.shrinkwrap.descriptor.api.Descriptors; | |
import org.jboss.shrinkwrap.descriptor.api.webapp30.WebAppDescriptor; | |
import org.jboss.shrinkwrap.descriptor.api.webcommon30.WebAppVersionType; | |
import org.junit.Test; | |
import static org.junit.Assert.*; | |
import org.junit.runner.RunWith; | |
import org.openqa.selenium.WebDriver; | |
/** | |
* Checks the {@link HeaderFilter} initialization parameter set the disabled to | |
* confirm that headers are not added to the {@link HttpServletRequest}, or | |
* {@link HttpServletResponse}. | |
* | |
* @author John Yeary | |
* @version 1.0 | |
* @see HeaderFilterDisabledIntegrationTest | |
*/ | |
@RunWith(Arquillian.class) | |
@WarpTest | |
@RunAsClient | |
public class HeaderFilterRegistrationIntegrationDisabledTest { | |
public static final String URL_PATTERN = "/*"; | |
public static final String PARAM_NAME = "enabled"; | |
public static final String PARAM_VALUE = "false"; | |
@ArquillianResource | |
private URL contextPath; | |
@Drone | |
private WebDriver webdriver; | |
/** | |
* Default constructor. | |
*/ | |
public HeaderFilterRegistrationIntegrationDisabledTest() { | |
} | |
/** | |
* Creates a {@link WebArchive} for auto-deployment in Arquillian. | |
* | |
* @return an Web ARchive (war) file representing the filter to be tested. | |
*/ | |
@Deployment | |
public static WebArchive createDeployment() { | |
WebArchive war = ShrinkWrap.create(WebArchive.class) | |
.addClass(HeaderFilter.class) | |
.setWebXML(generateWebXMLAsset()); | |
System.out.println(war.toString(true)); | |
return war; | |
} | |
/** | |
* This builder constructs a web.xml descriptor for a filter and converts it | |
* to a {@link StringAsset}. | |
* | |
* @return the web.xml represented as an {@link Asset}. | |
*/ | |
private static Asset generateWebXMLAsset() { | |
WebAppDescriptor wad = Descriptors.create(WebAppDescriptor.class); | |
wad.version(WebAppVersionType._3_0) | |
.createFilter() | |
.filterName(HeaderFilter.class.getSimpleName()) | |
.filterClass(HeaderFilter.class.getName()) | |
.createInitParam() | |
.paramName(PARAM_NAME) | |
.paramValue(PARAM_VALUE) | |
.up() | |
.up() | |
.createFilterMapping() | |
.filterName(HeaderFilter.class.getSimpleName()) | |
.urlPattern(URL_PATTERN) | |
.up(); | |
System.out.println(wad.exportAsString()); | |
return new StringAsset(wad.exportAsString()); | |
} | |
/** | |
* Executes the doFilter() method of the filter and checks the expected | |
* results. | |
*/ | |
@Test | |
public void testDoFilter() { | |
Warp.initiate(new Activity() { | |
@Override | |
public void perform() { | |
System.out.println("TEST URL: " + contextPath.toString()); | |
webdriver.navigate().to(contextPath); | |
} | |
}).inspect(new Inspection() { | |
private static final long serialVersionUID = 1489207995240215531L; | |
@ArquillianResource | |
HttpServletRequest request; | |
@ArquillianResource | |
HttpServletResponse response; | |
@BeforeServlet | |
public void before() { | |
List attributeNames = Collections.list(request.getAttributeNames()); | |
System.out.println("Attributes: " + Arrays.toString(attributeNames.toArray())); | |
assertNull(request.getAttribute("X-ATTRIBUTE")); | |
} | |
@AfterServlet | |
public void after() { | |
System.out.println("Headers: " + Arrays.toString(response.getHeaderNames().toArray())); | |
assertNull(response.getHeader("X-HEADER")); | |
} | |
}); | |
} | |
} |
0 comments :
Post a Comment