5

I have several wicket tests that target a sortable DataTable, specifically ajax-clicking the sortable column headers and asserting the contents of the rendered body rows. Now the component hierarchy of the table component's descendants is auto generated by the wicket framework, and results in paths to the sorting links (ajax) similar to:

table:topToolbars:toolbars:0:headers:1:header:orderByLink

However, when the DataTable gets re-rendered across tests, the index of the toolbars component is incremented each time i.e similar to:

table:topToolbars:toolbars:1:headers:1:header:orderByLink

which then breaks the hard-coded paths of the succeeding tests as they will no longer match.

The code fragment for the datatable construction is as follows:

        final PayeesProvider dataProvider = new PayeesProvider();
    table = new DataTable<ResponsePayeeDetails>("payees", columns, dataProvider, rowsPerPage);
    table.setOutputMarkupId(true);
    table.addTopToolbar(new AjaxFallbackHeadersToolbar(table, dataProvider) {

        private static final long serialVersionUID = -3509487788284410429L;

        @Override
        protected WebMarkupContainer newSortableHeader(final String borderId, final String property, final ISortStateLocator locator) {
            return new AjaxFallbackOrderByBorder(borderId, property, locator, getAjaxCallDecorator()) {

                @Override
                protected void onRender() {
                    System.out.printf("Path: %s\n", this.getPageRelativePath());
                    super.onRender();
                }

                private static final long serialVersionUID = -6399737639959498915L;

                @Override
                protected void onAjaxClick(final AjaxRequestTarget target) {
                    target.add(getTable(), navigator, navigatorInfoContainer);
                }

                @Override
                protected void onSortChanged() {
                    super.onSortChanged();
                    getTable().setCurrentPage(0);
                }
            };
        }
    });
    table.addBottomToolbar(new NoRecordsToolbar(table));
    add(table);

To be precise, when I run my tests, the above System.out.printf statement prints:

(1st test)

Path: payees:topToolbars:toolbars:0:headers:1:header
Path: payees:topToolbars:toolbars:0:headers:2:header

(2nd test)

Path: payees:topToolbars:toolbars:2:headers:1:header
Path: payees:topToolbars:toolbars:2:headers:2:header

(3rd test)

Path: payees:topToolbars:toolbars:4:headers:1:header
Path: payees:topToolbars:toolbars:4:headers:2:header

(4th test)

Path: payees:topToolbars:toolbars:6:headers:1:header
Path: payees:topToolbars:toolbars:6:headers:2:header
Path: payees:topToolbars:toolbars:6:headers:1:header
Path: payees:topToolbars:toolbars:6:headers:2:header
Path: payees:topToolbars:toolbars:6:headers:1:header
Path: payees:topToolbars:toolbars:6:headers:2:header

(5th test)

Path: payees:topToolbars:toolbars:8:headers:1:header
Path: payees:topToolbars:toolbars:8:headers:2:header

Does anyone know how I can force the index generation to be more deterministic / repeatable. Alternatively, is there a way of wild-carding or otherwise generalising the path, so as to make them immune to these increments?

Any help will be greatly appreciated chaps!

Michael-7
  • 1,739
  • 14
  • 17

2 Answers2

0

Determining the path can be quite hard but shouldn't be necessary, as this would consist an integration-test and not an unit-test. Testing the sorting of a column shouldn't depend on the surrounding table, so you could create a dummy-table in your testcase adding the header and one column (the one to be sorted) for the test. If you have the toolbar-object, you can either use it right away or re-retrieve it by issuing getComponent(toolbar.getPageRelativePath()). But ensuring that an AjaxFallbackOrderByBorder actually works shouldn't be your concern as a user of this class. This should have been done by the creator of the class. You'd only have to test your own code (like the sorting-code in your data provider)...

Nicktar
  • 5,548
  • 1
  • 28
  • 43
  • Thanks Nicktar, I am not sure I completely follow. What I am trying to implement is somewhat akin to an integration test - the slight difference being that the service layer is all mock'ed. So to ensure that all components - UI and non-UI - are wired correctly, I fire gestures via the rendered widgets and verify the resultant page fragments. I could, as you suggest, verify the re-generated models, but this might exclude testing that the same models are configured and attached correctly to the UI across request roundtrips. Or have I missed your point? – Michael-7 May 11 '12 at 23:19
  • @Michael-7 My point was that this test shouldn't be nessecary, at least not in the form of an UnitTest. I'd separate UnitTests from integration tests, running the formers by jUnit, checking models ans stuff and running the later from "high above" using selenium ot something similar, checking the rendered pages... – Nicktar May 12 '12 at 16:31
  • Thanks for your feedback Nicktar. The reason I use wicketTester is to avoid having to use selenium which is fine but cumbersome - i.e. having to start up a server e.t.c. WicketTester is exactly what I need to test my server-side components, specifically the server-side end-to-end roundtrip as I do with the rest of my code. I must emphasize this is not a unit test, but rather an integration test with the service layer stubbed out. – Michael-7 May 15 '12 at 04:00
0

The id's are being incremented each time because of the default DataTable ItemReuseStrategy, which generates brand new items each time the table is refreshed. You may have some luck implementing a custom ItemReuseStrategy if keeping the id's the same is a top priority.

We ran into a similar issue and have settled on a strategy aligned to your alternative question. With this, you can ask the test to inspect e.g. the third row on the last rendered page. Note: Code is in Scala.

In short, to address this issue we implemented a findNthComponentOnPage method to augment the WicketTester.

/**
  * @param id Wicket id of component to find
  * @param n 1-based
  * @tparam T class of component to find
  * @return the Nth component of class T appearing on the lastRenderedPage
  */
def findNthComponentOnPage[T <: Component : ClassTag](id: String, n: Int): T = {
  tester.getLastRenderedPage.visitChildren(classTag[T].runtimeClass, new IVisitor[T, T] {
    var count = 0

    override def component(checkbox: T, visit: IVisit[T]): Unit = {
      if (checkbox.getId == id) {
        count += 1
        if (count == n) {
          visit.stop(checkbox)
        }

      }
    }
  })
}
Pat Brent
  • 16
  • 1
  • 4