Archive

Archive for August, 2009

Writing EVEN MORE readable and maintainable tests

August 25, 2009 2 comments

If you follow my blog you know that few weeks ago I posted an article about writing readable and maintainable tests. My understanding of readable and maintainable tests evolves in time, my views and opinions change and  thus my posts need update.

I thought that the easiest way to show you the changes will be an example. As before, examples are shown for Wicket application, however the rules can be applied to any other frontend frameworks (JSF applications can use for instance JSFUnit project). Secondly, if you haven’t read my article about readable and maintainable tests, I encourage you to do so, since this post is simply update of the previous one. Furthermore post about WicketTestBase should be helpful as well, since all examples inherit from WicketTestBase class.

But lets move on to our example. Imagine we are told to implement user story “Add new Applicant”.  General steps are as follows:

  1. User enters welcome page
  2. User clicks link called ‘create new applicant’
  3. User is given a form that he fills with Applicant data (name, surname and age)
  4. Users is presented with Applicants list, where newly added Applicant is visible
  5. Applicant is added to the Application he is applying to

Before I explain you all the  necessary details, please let me show you how would the final tests for this scenario look like. I want you to see them now, just to let you know how readable tests can really be. For the purpose of this  example I will only show two tests, however it should be obvious that in real life overall number of tests can be much bigger. The tests look as follow:

test #1  – Should show ApplicantsList after adding applicant

public void shouldShowApplicantsListAfterAddingApplicant
                  throws Exception {
 // given
 user.enters().welcomePage();
 user.clicks().createNewApplicationLink();

 // when
 user.fills().addApplicantForm("Adam", "Bauer", 27);
 user.clicks().saveAndExitButton();

 // then
 onpage.applicantsList.exists().isVisible().and().isOfType(ListView.class);
}

test #2 – Should show added applicant informations

public void shouldShowAddedApplicantInformations
                  throws Exception {
 // given
 user.enters().welcomePage();
 user.clicks().createNewApplicationLink();

 // when
 user.fills().addApplicantForm("Adam", "Bauer", 27);
 user.clicks().saveAndExitButton();

 // then
 onpage.applicantsList
            .containsElements(application.getApplicants());
 onpage.applicantsList
            .element(0, "appliocantName").isLabelWithValue("Adam");
 onpage.applicantsList
            .element(0, "appliocantSurname").isLabelWithValue("Bauer");
 onpage.applicantsList
            .element(0, "appliocantAge").isLabelWithValue("27");
}

Readable isn’t it? The tests themselves can be read as a normal sentences. They should give you this feeling that you are reading a book. This is what self-documenting code is all about. When you read the test, it is quite clear what the implementation should do. Test are understandable, test base is comprehensive. And you know what? It will still be, even after one year. Or maybe you are soon planning to hire new developer? Not a problem at all, he will easily understand them as well. That is what all the readability is all about!
Notice that in fact when reading those tests you are not even aware of the fact that they are testing Wicket application. Remember that tests should always represent the requirements! If those test fail and fixing them is not a matter of change in one or two lines of code, then it only means the requirements changed. However if the button gets different name or ApplicantsList suddenly gets different path, then fixing those tests is simply change in one line of code. This is what all the maintainability is all about!

The question remains then, what changes were applied. If you were asked to write similar tests and you would follow my previous guidelines, you would probably end up with something like this:

test #1 – Should show ApplicantsList after adding applicant

public void shouldShowApplicantsListAfterAddingApplicant
                  throws Exception {
 // given
 enterer.entersWelcomePage();
 clicker.clickCreateNewApplicationLink();

 // when
 formFiller.fillsAddApplicantForm("Adam", "Bauer", 27);
 clicker.clicksSaveAndExitButton();

 // then
 String pathToApplicantsList = "path:to:applicantslist";
 tester.getComponentFromLastRenderedPage(pathToApplicantsList);
 tester.assertVisible(pathToApplicantsList);
 tester.assertComponent(pathToApplicantsList, ListView.class);
}

test #2 – Should show added applicant informations

public void shouldShowAddedApplicantInformations
                  throws Exception {
 // given
 enterer.entersWelcomePage();
 clicker.clickCreateNewApplicationLink();

 // when
 formFiller.fillsAddApplicantForm("Adam", "Bauer", 27);
 clicker.clicksSaveAndExitButton();

 // then
 String pathToApplicantsList = "path:to:applicantslist";
 tester.assertListView(pathToApplicantsList, application.getApplicants());
 tester.assertLabel(pathToApplicantsList + "applicantName", "Adam");
 tester.assertLabel(pathToApplicantsList + "applicantSurname", "Bauer");
 tester.assertLabel(pathToApplicantsList + "applicantAge", "27");
}

It’s good already, but it has its flaws. First lets look at helper classes, they great at aggregating common actions (entering pages, clicking and form filling), but when we read the test

 (...)
 // given
 enterer.entersWelcomePage();
 clicker.clickCreateNewApplicationLink();

 // when
 formFiller.fillsAddApplicantForm("Adam", "Bauer", 27);
 clicker.clicksSaveAndExitButton();
(...)

it doesn’t sound right. Try to read above code out loud. If you do that, you will quickly learn that there is something missing. Lets try it, read this out loud:

“Enterer enters welcome page”
“Clicker clicks create new application link”
“FormFiller fills add applicant form”

What is wrong with it? Why doesn’t it sound right? Well we basically miss person who is responsible for all the clicking, entering and form filling. We miss here the User! Whenever I read test, I want to hear sentences like this:

User enters welcome page”
User clicks create new application link”
User fills add applicant form”

I mean who is this enterer, what is this clicker, were did this formFiller came from? It is User who does the whole work. Clicker, Enterer and FormFiller are just representations of the actions he does. Taking that fact into consideration I created another helper class called Userer.

public class Userer {

  private Clicker clicker;
  private Enterer enterer;
  private FormFiller formFiller;

  public Userer(Clicker clicker,
                Enterer enterer,
                FormFiller formFiller) {
      this.clicker = clicker;
      this.enterer = enterer;
      this.formFiller = formFiller;
  }

  public Clicker clicks() {
      return clicker;
  }

  public FormFiller fills() {
      return formFiller;
  }

  public Enterer enters() {
      return enterer;
  }
}

This class simply aggregates Clicker, FormFiller and Enterer. Userer becomes field in WicketTestBase (base class for all my Wicket tests) as shown below:

public class WicketTestBase {

    (...)
    protected Clicker clicker;
    protected Enterer enterer;
    protected FormFiller formFiller;

    protected Userer user;
    (...)

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

From now on you can use this ‘user’ field in your tests. Now we can refactor our test:

  1. calls to clicker become calls to user.clicks() e.g clicker.clicksSaveLink becomes user.clicks().saveLink() (notice we also changed clickSaveLink method name to saveLink)
  2. calls to formFiller become calls to user.fills() e.g formFiller.fillsAddApplicantForm(“Adam”, “Bauer”, 27) becomes user.fills().addApplicantForm(“Adam”, “Bauer”, 27)
  3. calls to enterer become calls to user.eneters() e.g enterer.entersWelcomePage() becomes users.enters.welcomePage()

Now our previous test will look like this:

test #1 – Should show ApplicantsList after adding applicant

public void shouldShowApplicantsListAfterAddingApplicant
                  throws Exception {
 // given
 user.enters().welcomePage();
 user.clicks().createNewApplicationLink();

 // when
 user.fills().addApplicantForm("Adam", "Bauer", 27);
 user.clicks().saveAndExitButton();

 // then
 String pathToApplicantsList = "path:to:applicantslist";
 tester.getComponentFromLastRenderedPage(pathToApplicantsList);
 tester.assertVisible(pathToApplicantsList);
 tester.assertComponent(pathToApplicantsList, ListView.class);
}

test #2 – Should show added applicant informations

public void shouldShowAddedApplicantInformations
                  throws Exception {
 // given
 user.enters().welcomePage();
 user.clicks().createNewApplicationLink();

 // when
 user.fills().addApplicantForm("Adam", "Bauer", 27);
 user.clicks().saveAndExitButton();

 // then
 String pathToApplicantsList = "path:to:applicantslist";
 tester.assertListView(pathToApplicantsList, application.getApplicants());
 tester.assertLabel(pathToApplicantsList + "applicantName", "Adam");
 tester.assertLabel(pathToApplicantsList + "applicantSurname", "Bauer");
 tester.assertLabel(pathToApplicantsList + "applicantAge", "27");
}

Pretty readable right? All looks good, but imagine what will happen, when path to ApplicationList change or it will become PropertyListView (instead of ListView)? Some time ago I realized that every action of the test scenarios falls into one of four categories. User either enters application, clicks a link or button, fills form with data or assert state of the application. For entering, filling and clicking I created helper classes that encapsulate those actions (Enterer, FormFiller and Clicker), however assertion still did not get its representation. Thus I created another helper class called Weberer.

public class Weber {

    // components
    public PageElement applicantsList;

    // testers
    private WicketTester tester;
    private EnhancedWicketTester enhancedWicketTester;

    public Weber(WicketTester tester,
                         EnhancedWicketTester enhancedWicketTester) {
        this.tester = tester;
        this.enhancedWicketTester = enhancedWicketTester;

        createPageElements(tester, enhancedWicketTester);
    }

    private void createPageElements(WicketTester tester,
                                    EnhancedWicketTester enhancedWicketTester) {
        applicantsList = new PageElement("applicationForm:panel:listApplicantsPanel:applicants",
                                         tester, enhancedWicketTester);
    }
}

This class represents informations presented on page and helps to perform various assertion actions on those objects. Weberer holds reference to public fields of type PageElement.

public class PageElement implements IPageElement {

    private final WicketTester tester;
    private final EnhancedWicketTester enhancedWicketTester;
    private final String path;

    public PageElement(String path, WicketTester tester, EnhancedWicketTester enhancedWicketTester) {
        this.tester = tester;
        this.enhancedWicketTester = enhancedWicketTester;
        this.path = path;
    }
    public PageElement exists() {
        tester.getComponentFromLastRenderedPage(path);
        return this;
    }

    public PageElement isVisible() {
        tester.assertVisible(path);
        return this;
    }

    (....)

Those fields represent different elements that can be found on page. So for example if suddenly you need to make some assertions on login form, then you will add to Weberer object new field:

public class Weber {

    // components
    public PageElement loginForm;
    (...)

    private void createPageElements(WicketTester tester,
                                                         EnhancedWicketTester enhancedWicketTester) {
        loginForm = new PageElement("path:to:login:form",
                                                            tester, enhancedWicketTester);
        (...)
    }

Since Weberer is also a filed in WicketTestBase class:

public class WicketTestBase {

    (...)
    protected Userer user;
    protected Weber onpage;
    (...)

you can access your newly created element directly from your tests:

   onpage.loginForm.exists();
   onpage.loginForm.isVisible();

When we apply those rules into our test, they will final look like this:

test #1 – Should show ApplicantsList after adding applicant

public void shouldShowApplicantsListAfterAddingApplicant
                  throws Exception {
 // given
 user.enters().welcomePage();
 user.clicks().createNewApplicationLink();

 // when
 user.fills().addApplicantForm("Adam", "Bauer", 27);
 user.clicks().saveAndExitButton();

 // then
 onpage.applicantsList.exists().isVisible().and().isOfType(ListView.class);
}

test #2 – Should show added applicant informations

public void shouldShowAddedApplicantInformations
                  throws Exception {
 // given
 user.enters().welcomePage();
 user.clicks().createNewApplicationLink();

 // when
 user.fills().addApplicantForm("Adam", "Bauer", 27);
 user.clicks().saveAndExitButton();

 // then
 onpage.applicantsList
            .containsElements(application.getApplicants());
 onpage.applicantsList
            .element(0, "appliocantName").isLabelWithValue("Adam");
 onpage.applicantsList
            .element(0, "appliocantSurname").isLabelWithValue("Bauer");
 onpage.applicantsList
            .element(0, "appliocantAge").isLabelWithValue("27");
}

The test code is now easy to read and easy to maintain. All the Wicket specific code is hidden behind those helper classes. This not only helps to maintain test in a short time (when for example path to some components change) but also in a long run (when for example API of the WicketTester change in the 1.5 version). It will be lot easier to apply those changes only to 5 helper classes, then to base of few hundred tests already implemented in your application.

All code presented here will be soon (in a matter of days) added to WicketCool project, so you will be able to see WicketTestBase in action.

Advertisements

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
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

Headache Pill: Unable to find valid certification path to requested target

August 5, 2009 Leave a comment

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: ,