Archive

Posts Tagged ‘wickettester’

WicketTestBase – base class for testing Wicket + Spring web applications

August 17, 2009 5 comments

When writing integration tests in Wicket, there are few initialization steps that need to be executed before actual testing:

  1. data initialization – tests often need static data in the database for each and every test method.
  2. tester initialization – tester is initialized with Application and Session object, given locale needs to be set, Spring injector needs to be pre-configured etc.
  3. helper class initialization

Putting those actions to some init method (annotated with @Before in JUnit) would be a good idea. However I thought that since those steps are done in every single test, smart thing to do is moving them up in class hierarchy into some super class.
This is how WicketTestBase was created. Base class for integration tests in Wicket + Spring applications. Let’s take a look at it:

public class WicketTestBase {

 @Autowired
 private ApplicationContext applicationContext;

 protected WicketTester tester;
 protected EnhancedWicketTester enhancedTester;
 protected Clicker clicker;
 protected Enterer enterer;
 protected FormFiller formFiller;
 (...)

WicketTestBase has six protected fields. Spring’s ApplicationContext is injected, other five fields (tester, enhanced tester and helper classes) need to be initialized for every test. All fields have protected access level,  thus they can be reached from test classes that will extend WicketTestBase.

If you haven’t heard about enhanced tester, then please follow this article. If Clicker, FormFiller and Enterer are new to you, then kindly check my article about readable tests.

public void init() {
 populateData();
 createTester();
 initHelpers();
}

Next we have the public init method. This method will be called with every test. Method populateData is empty by default, but can be extended in test classes inheriting from WicketTestBase.

 /**
  * Override to populate data in database
  * for each test
  */
 protected void populateData() {
     // override in test if necessary
 }

Method createTester is the most complex one, but still it should be clear what it does.

private void createTester() {
 tester = new WicketTester((WebApplication)
 applicationContext.getBean("wicketApplication"));
 tester.setupRequestAndResponse();
 tester.getWicketSession().setLocale(getLocale());
 tester.getApplication().addComponentInstantiationListener(
 new SpringComponentInjector(tester.getApplication(),
 applicationContext));
 enhancedTester = new EnhancedWicketTester(tester);
 initSessionBeforeTest((UserSession) tester.getWicketSession());
}

First WicketTester object is created with Wicket Application object as constructor’s parameter. Note that Wicket Application object is taken from Spring’s ApplicationContext. Then Spring pre-configuration takes place and at the end Enhanced WicketTester is created.

Note also that two hook methods are called: getLocale and initSessionBeforeTest.

/**
 * Override to change locale
 * @return locale, default EN
 */
 protected Locale getLocale() {
 return new Locale("EN");
 }

Method getLocale returns default local for the tests. By default it returns EN, but extending classes can override this method to provide different locale.

 /**
 * Insert application specific properties to session
 * @param session
 */
 protected void initSessionBeforeTest(UserSession session) {
 // override in test if necessary
 }

Method initSessionBeforeTest is empty by default. It should be overridden if you would like to insert any test specific data into session before each test.

At the end helper classes are initialized.

private void initHelpers() {
 clicker = new Clicker(tester, enhancedTester);
 formFiller = new FormFiller(tester, enhancedTester);
 enterer = new Enterer(tester, enhancedTester, clicker, formFiller);
}

And that is it. Below WicketTestBase in its full glory:

public class WicketTestBase {

 @Autowired
 private ApplicationContext applicationContext;

 protected WicketTester tester;
 protected EnhancedWicketTester enhancedTester;
 protected Clicker clicker;
 protected Enterer enterer;
 protected FormFiller formFiller;

 public void init() {
     populateData();
     createTester();
     initHelpers();
 }

 private void createTester() {
     tester = new WicketTester((WebApplication)
                        applicationContext.getBean("wicketApplication"));
     tester.setupRequestAndResponse();
     tester.getWicketSession().setLocale(getLocale());
     tester.getApplication().addComponentInstantiationListener(
                      new SpringComponentInjector(tester.getApplication(),
                      applicationContext));
     enhancedTester = new EnhancedWicketTester(tester);
      initSessionBeforeTest((UserSession) tester.getWicketSession());
 }

 private void initHelpers() {
    clicker = new Clicker(tester, enhancedTester);
    formFiller = new FormFiller(tester, enhancedTester);
    enterer = new Enterer(tester, enhancedTester, clicker, formFiller);
 }

 /**
  * Override to change locale
  * @return locale, default EN
  */
 protected Locale getLocale() {
     return new Locale("EN");
 }

 /**
  * Insert application specific properties to session
  * @param session
  */
  protected void initSessionBeforeTest(UserSession session) {
     // override in test if necessary
  }

 /**
  * Override to populate data in database
  * for each test
  */
 protected void populateData() {
     // override in test if necessary
 }
}

Simplest example of test extending from WicketTestBase will look like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:testApplicationContext.xml"})
@Transactional
public class NameOfTheTest extends WicketTestBase {

    @Before
    public void init() {
        super.init();
    }

}

ContextConfiguration points to Spring’s configuration xml file (in this case testApplicationContext.xml) and Transactional annotation simply makes all test methods transactional. Worth mentioning is the fact that all this transactions are rolled back after each test method finish it’s execution, thus you won’t have to implement any destroyData method – this is automatically done for you.

Usage of WicketTestBase can be also found in WicketCool project. If interested, check it out.

see also:

  1. Writing readable and maintable integration tests (not only) in Wicket
  2. Test template you should always use
Advertisements
Categories: Wicket Tags: ,

Writing readable and maintainable integration tests (not only) in Wicket

August 12, 2009 6 comments

Enhanced WicketTester

August 5, 2009 6 comments

Wicket testing pitfalls and API issues

August 3, 2009 1 comment

WicketTester is so cool

WicketTester is a very powerful tool.  It gives ability to easily write robust and accurate integration tests and in my opinion makes Apache Wicket (with combination of some backend technology like Spring) the best framework on the market for TDD development. And I strongly stand for what I just said. I just simply know that from my very own experience:  currently ~90% of requirements in my recent Wicket project were developed using TDD practice.

WicketTester is not that cool

However despite all that, there is another side to that story. WicketTester is not a first class citizen in Apache Wicket framework. Even its developers agrees that “currently WicketTester is one of the less mature parts of the core project”.  And I’m not talking about its inner implementation, because as an end user, my real concern are two things: does it work correctly and does it has readable API (which will make my tests readable)? Unfortunately, for WicketTester, that is not always the case. Let me show you why.

1. Pitfalls

1.1 Invisible and disabled forms

Let’s say that for example you have a sign-in form, and you are writing a test for it. The use case is rather simple:  we enter sing-in page, fill login field with value “admin” and after submitting  form, UserSession object should hold reference to the User object with login “admin”.

@Test
public void shouldLogInAdminUser()
             throws Exception {
 // given
 enterer.enterSigninPage();

 // when
 FormTester formTester = tester.newFormTester("siginForm");
 formTester.setValue("login", "admin");
 formTester.submit("signInButton");

 // then
 assertEquals("admin",
              UserSession.get().getUser().getLogin());
}

Looks rather ok, ain’t it? But what you think would happen, if TextField “login” was invisible at that moment? For me, obvious behavior would be some sort of exception on line 09, setting the value to invisible component is impossible in real life, thus it should be impossible in test. Unfortunately the tests goes on without any warning and fails on line 13. Because login field was invisible, setting User.login value to “admin” didn’t occur and so the User was never added to the UserSession.

Now if you think, this is very trivial scenario, try to imagine what would you do, if situation like this happened in your code. You have written test, checking whether admin user is logged in with value “admin”, but the test fails on assertion. I guarantee that the first thing you do,  would be checking the sign-in logic.  It takes time and some debugging to realize that login field was simply invisible. Method setValue did not perform any action, but also did not raise any exception.  Finding this sort of bug would take some time for this simple scenario, but believe me, it can be long and frustrating when dealing with it for the first time, on page with large form, divided and organized on different panels.

Note also that this situation will also occur, if the whole Form class object is invisible. What makes it even worse is the fact, that invisibility is not the only issue. If your form would be disabled or had disabled fields, tests will act in same way. They won’t raise exceptions, but they won’t set values either.

1.2 Setting selectable

Imagine that on the sign-in page long time ago you entered another TextField called ‘select’. You even wrote a test for it.

@Test
public void shouldReadSelectField() throws Exception {

 // given
 enterer.enterSigninPage();

 // when
 FormTester formTester = tester.newFormTester("siginForm");
 formTester.setValue("select", "somevalue");
 formTester.submit("signInButton");

 // then
 // some assertions here

}

But after a while some other developer decided that ‘select’ should not be TextField but DropDownChoice. He made preferable changes in implementation, however he totally forgot about tests (I know, ignorant! ;)). Nevertheless you should not be worried, right? I mean, some sort of exception should be thrown at line 09, since method select() should be used, not setValue. Again however, no such exception occurs.

Method setValue itself is not doing anything (since it is not possible to setValue to the DropDownChoice). Given test would fail or not (depending on the assertions), but still it could take some time to figure out what happened.

1.3 I’m a SubmitLink you moran!

Another scenario is when you have some sort of Link object. Let’s say that the link is on WelcomePage and should redirect to SignInPage. As usual you implement logic in onClick() method, simple setResponse(SignInPage.class) will suffice. The test for this scenario could look like this.

@Test
public void shouldKnowWhenDealingWithSubmitLinkNotLink()
          throws Exception {
 // given
 enterer.enterWelcomePage();

 // when
 tester.clickLink("signinLink");

 // then
 tester.assertRenderedPage(SignInPage.class);
}

But what would happen if someone changed the Link into SubmitLink? For the moment you might think, that there should not be any problem since SubmitLink extends Link class. However notice that chances are, that switching from Link to SubmitLink would probably  trigger movement logic from onClick() method to onSubmit() method.

This way tester.clickLink() will click the link, but no action will happen, since all the logic will be moved out from the onClick method. The test in almost most cases will fail. In this trivial example, it should be rather easy to find out why, but if you have a little bit larger test, were clicking the link is not the action that is being tested (links are just being clicked to reach to some state of application), then finding out why suddenly test fails can take few moments.

1.4. Wicket 1.3.x problems

Some issues present in 1.3.x release of the Wicket framework, where fortunately fixed in the 1.4 branch. Since recent release of Wicket 1.4, below described problems might be soon obsolete. Nevertheless it will take some time, for all Wicket project to switch from 1.3.x to 1.4 version, thus I will try to briefly describe them.

setting not existing TextField

Lets go back to our first example:

@Test
public void shouldLogInAdminUser()
             throws Exception {
 // given
 enterer.enterSigninPage();

 // when
 FormTester formTester = tester.newFormTester("siginForm");
 formTester.setValue("lgin", "admin");
 formTester.submit("signInButton");

 // then
 assertEquals("admin",
              UserSession.get().getUser().getLogin());
}

Notice that on line 09 I made a typo, name of the field should be “login” not “lgin”. Expecting Exception thrown at this point my friend? This won’t happen! Test simply goes on, without any warning, but field is never filled with value. Issue fixed in 1.4 branch, throwing appropriate exception at line 09.

clicking not existing Button
@Test
public void shouldLogInAdminUser()
             throws Exception {
 // given
 enterer.enterSigninPage();

 // when
 FormTester formTester = tester.newFormTester("siginForm");
 formTester.setValue("login", "admin");
 formTester.submit("sgnInButton");

 // then
 assertEquals("admin",
              UserSession.get().getUser().getLogin());
}

Same thing will happen if you have typo in your submit button (here on line 10). Button is never clicked, since it does not exist, but no exception is thrown either. Again issue was fixed in branch 1.4.

2. API issues

2.1 Clicking link, clicking AJAX link

Imagine you are seeing Wicket test for the very first time in your life. You’ve never programmed in Wicket before and yet someone gives you test, that contains this line of code:

 tester.clickLink("adminPageLink");

Pretty straightforward, right? One can clearly see that we are clicking adminPageLink. But imagine that later on, you see this:

 tester.clickLink("openSubPanelLink", true);

Ok, we are clicking openSubPanelLink, but what does second, boolean parameter means? One have to look up documentation to realize that we are clicking link in AJAX manner. But honestly, would not be much simpler  and more readable to just call

 tester.clickAjaxLink("openSubPanelLink");

Method clickAjaxLink could even do some validation, checking whether link that is being clicked, is of AJAX type.

2.2 Assert enable, assert disable

WicketTester gives this assert methods like assertVisible, assertInvisible. What really strikes me is lack of methods such as assertEnabled and assertDisabled. I mean, lots of my components are visible or hidden in my application and my tests need to check that, but I also have a lot of fields that are being enabled and disabled. Why not then assertEnabled and assertDisabled? Strange but I need to cope with that.

Summery

To sum up, I love WicketTester. Without it my productivity in Wicket would be much much lower. Described pitfalls and API issues are what I learned from my 5 months experience with Wicket. Pitfalls were source of few hours of debugging, I hope with this post, you won’t have to waste your time on them.

You might also want to read about:

  1. Enhanced WicketTester
Categories: Wicket Tags: ,