WicketTester and Spring

One thing that's always bugged me about testing my wicket applications is that using WicketTester with a Spring-based Application isn't the most enjoyable process. I have been using Selenium to test my applications and that works sort of OK. Selenium has problems detecting which link I click if there are a lot of similar links and since most links in Wicket encode state/version information, you can also have "link drift" so that even when the link is properly recorded, the versioning information might change in a long running session and then Selenium loses the plot. In addition, Selenium tests aren't so simple to run as part of a Hudson run if you're not running Hudson in a windowed environment. So it's not perfect, but I've used it about as well as I can for a while now. But I really wanted that WicketTester option working for me. So I sat down and figured it out. I'm working with Wicket 1.3 snapshots so you'll need at least beta1 to do it this way. Also, I have some database population code in my application's init to prepopulate certain data elements after initial installation so it complicates the scenario a little but not overly much. Also, I'm using TestNG in this example and a slightly modified/cleaned up version of the testng-spring classes. With that set up out of the way, let's get to some code.

The path to this code was rather frustrating once I sat down to actually figure it out, but the solution turns out to be rather simple. My tests extend AbstractTransactionalSpringContextTests and override two methods:

@Override protected String[] getConfigLocations() { return LOCATIONS; }

@Override protected void prepareTestInstance() throws Exception { inject(); setTransactionManager(transactionManager); }

If you've used spring with either junit or testng, the first method is simple enough. It just lets the test harness know where to find the spring config files. This method is called by the testng classes and all you need to worry about is to properly define that String[] to list your config file(s). In my case, that's

private static final String[] LOCATIONS = { "classpath:applicationContext.xml", "classpath:applicationContext-test.xml" };

The second method, I'll admit, might be a set up issue with my environment, but I had no TransactionManager defined so none of my tests would start a transaction which obviously caused a few problems. That transactionManager field is injected by the testng-spring infrastructure from the same TM used by my application at runtime.

So then, back to the actual test class. I initialize my tests with this:

@BeforeMethod public void init() { tester = new WicketTester(new TestMyApplication()); }

@Override protected ConfigurableApplicationContext loadContextLocations(String[] locations) throws Exception { ++this.loadCount; XmlWebApplicationContext context = new XmlWebApplicationContext(); context.setConfigLocations(locations); context.refresh(); return context; }

private class TestMyApplication extends MyApplication { @Override public void init() { ServletContext servletContext = getServletContext(); XmlWebApplicationContext context = (XmlWebApplicationContext)applicationContext; servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context); context.setServletContext(servletContext); super.init(); } }

I create the WicketTester using a subclasses version of my actual Application. Then I override the testng method to create the application context to make sure the actual implementation matches what wicket-spring will be looking for. The default type is ClassPathXmlApplicationContext which isn't quite what I need. Then I subclass my application and override init() to fetch that application context and add it to the MockServletContext that WicketTester creates. Then I super up to my "real" init() and let things proceed from there.

And that's about it. I use the OpenEntityManagerInView from spring which handles all this for me when I run the application in jetty. But lacking that infrastructure under testng/wickettester, I've had to emulate portions of it and that's what you see here. I just got all this together in the last day or two so there are likely some subtleties missing but it's worked so far for me. Please try this out if you're in a similar situation and if you find missing pieces, please do let me know. I'll keep you updated if I find anything. Enjoy.

JasperReports in Action update

My apologies for the lack of any updates on this front. Things have been crazy busy with the move and the new job and this just dropped off my radar for reasons I'll now share. It is with mixed feelings that I can say that the book has been killed. I'm mostly relieved about it but it's still a little sad. To understand why it was killed, you need to know a bit about its history. I was brought on the book about 1.5 years after it started. That's right 18 months. The publisher wasn't too happy with the state of things so I was brought in to help. Eventually because of some political stuff I won't get into here, I was bumped to primary author and a new coauthor brought on board. That author dropped off because of overcomitment with work issues. Throughout all this the publisher was getting increasingly nervous about the book's viability. While myself and my editor believed there is a decent market for the book, the publisher kept asking us to get a feel for its marketability. My own interest in the project was waning since I never really signed on to be the primary author anyway and I was getting nervous about the publisher. We might convince him now to let us finish the book, but what if he decided to pull the plug just before it goes to print?

So with all that swirling around, I had a number of conversations with my editor (and she with the publisher) and decided that no one was passionate enough about the book to take the risk to finish the work only to have it killed at the last minute. So it was mutually decided to kill the project. So while it's sad to have put so much work on the project only to have it killed, it was an enormous (and ultimately unwanted) load off my back.

I still maintain the ownership of the manuscript (the parts I wrote at least) so I can release some of it. While I feel like I own most if not all of the current text, some of it comes from "editing" (basically rewriting as it turns out) the text. Still, though, in the interests of not upsetting the apple cart, so to speak, I probably won't be releasing those portions of the manuscript. However, there are a few chapters that are completely mine so I'll try to release those soon. Primarily, I'm thinking of the chapter on iReport here. These are rough, unedited first drafts so they likely won't be as pretty as a published book, but hopefully they'll be helpful.

So that's the short version of a long and tortured tale. My apologies to those waiting anxiously for its release but it's just not going to happen.

Is this a bug or am I just dumb?

You know it's been too long since you've blogged when you can't remember how to login. Since my post in January, however, I've sold my house in Denver, sold my cars, packed up the wife and kids and moved to Brooklyn. I accepted a full-time, permanent position in Manhattan almost 3 months ago and I've been terribly busy transitioning jobs and cities. Like most bloggers, I've been meaning to get back in the saddle for a while now but, you know how it is... Anyway! I don't normally spawn external processes from Java so I'm sure I'm missing some subtle points to this apparently fine art. While running tests to make sure that these processes correctly execute and return the expected data, I noticed that one call to a certain script file would hang intermittently. If I ran the script manully in a shell window, it would execute and terminate in less than a second. But from Java it would sit there for minutes before I finally killed. At first I thought maybe it was a bug in this utility I'm executing from the script. But since it runs fine in cmd.exe, that couldn't be it. So I eventually make my way to the Bug Parade where I find this entry.

Now to save you a little time, here's the skinny: When using Process.waitFor() sometimes it will block indefinitely until you read the data off the two InputStreams, i.e., the Process's stdout and stderr. Now, according to Sun engineers, this is not a bug despite the fact that the documentation makes no mention of this particular "feature." Now the astute observer might notice that this bug was filed and closed in 1999. So I guess my main question is, why isn't this better documented if Sun decided this was acceptable behavior 8 years ago?

So, is this really (still) a bug for am I just dumb?