Iterating over Tables in a native Mac (Cocoa) GUI Test

Iterating over Tables in a native Mac (Cocoa) GUI Test

Neha wrote a blog article about Iterating over Tables in a Qt GUI Test a couple of weeks ago. I want to do the same in this blog article with the Squish for Mac and a Cocoa application. The application uses the NSTableView class for displaying tables.


As Neha pointed out, there are occasions when you want to iterate over the items in a table. The table verification points in Squish are a good solution if you want to verify the whole content of a table. But if you only want to look for specific items, then you need a more flexible solution. Or if you want to verify more complex conditions than simple equality.

Simple Approach

The items in a NSTableView table are children of the object in Squish. We can get a list of the children in the test script with the object.children() function. The order of the items is as follows: first come all the items of the first column, then all items of the second column, etc. This ordering is maybe not what you expect, but for historic reasons Squish uses this ordering. And we keep this in order to not break compatibility with existing script.

We can also use determine the number of rows of a table by using Squish’s Objective-C binding: from the test script we can call the function -[NSTableView numberOfRows]. This information allows us to iterate over the children and calculate the row/column position of the item.

Putting all this into a Python script, we get:

    table = waitForObject(':Table Views_NSTableView')
    rowCount = table.numberOfRows()
    for i, child in enumerate(object.children(table)):
        row = i % rowCount
        column = i / rowCount
        test.log('row {} column {}: {}'.format(row + 1, column + 1, str(child)))

The main disadvantage of this approach is that object.children() fetches all objects at once. If you have a table with a lot of items, this might take too much time or too much memory.

More Efficient Approach For Larger Tables

So in case you have a large table, you might want to fetch just a single item in the loop instead of fetching all items upfront. This is especially more efficient if you might not need to iterate over all items of the table, but only a subset.

We could use the Objective-C API on NSTableView for this. But Apple’s APIs allow to feed the table in different ways with data (either using a data source or using bindings; and with bindings there are also different ways to do the binding). So you would need to know the implementation details of the table, in order to fetch the data.

Squish already has code that handles the different ways of filling the table with data. It uses the SquishProxy_TableViewItem class for this. This is something you don’t have to care about in general (it handles the items in the background for you). Since this class is usually not visible to the user, it is not documented in the Squish. But it is very convenient to use it for this particular use case:

    table = waitForObject(tablename)
    columnCount = table.numberOfColumns()
    rowCount = table.numberOfRows()
    for row in range(rowCount):
        for column in range(columnCount):
            item = SquishProxy_TableViewItem.proxyForTableView_row_column_(table, row, column)
            test.log('row {} column {}: {}'.format(row + 1, column + 1, str(item)))

In this example we iterate over all rows and all columns of the table and in the innermost loop we fetch the item for the row/column of the table. This works of course also without iterating and if you are interested in a single specific item.