Chaining page objects, or multiple possible page objects as a result of a single action

1.5k Views Asked by At

In web integration testing, page objects are expected to return other page objects as a result of some actions. For example, a LoginForm.submit() may return CustomerDashboard page object on success or a LoginFailed object on failure.

What I'm having difficulty understanding is what happens when the system is not quite that deterministic. For example Order.submit() may result in a OrderProcessing page, or an OrderProcessed page. What's the best way to handle such a scenario? Should Order.submit() return a tuple of possible PageObjects that then get handled in the individual test? What is the recommended approach here?

Update with an expanded problem statement

Imagine you have an shopping system that accepts orders. When submitting an order, the system may process the order immediately or the order may be placed in the queue. Eventually, the queued order gets processed. In Rubyish pseudocode the page-object objects responsible for testing this would be:

class ProcessedOrderPage < PageObject
    item order_id;
    item delivery_date;
end

class QueuedOrderPage < PageObject
    item orderInQueue;
end

class OrderPage < PageObject
    def submit_order_for_immediate_processing
        return ProcessedOrderPage.new
    end

    def submit_order_for_queued_processing
        return ProcessingOrderPage
    end
end

So then in our tests we can do things like:

processed_page = orderPage.submit_for_immediate_processing
assert_not_nil processed_page.order_id

or we can test the other scenario:

queued_page = orderPage.submit_for_queued_processing
assert_not_nil queued_page.orderInQueue

The idea here is that we know the expected behaviour exactly, so we can invoke the helpers that return the expected page object. In fact this is described pretty well in this question on SO.

I'm trying to handle a scenario where the behaviour is not so deterministic. Imagine that submit_for_immediate_processing and submit_for_queued_processing above are collapsed into a single method, submit_for_processing and the behaviour is determined by the runtime properties of the system. That is, the order is either processed immediately or is placed in the queue. This behaviour is not ideal from the testing standpoint, but it is what it is. I'd like to understand who is responsible for determining the outcome of this submit_for_processing method. In this particular case, I'm not really interested in the "queued" state anyway. I want to get to the point where the order is processed, so I can verify various order properties.

1

There are 1 best solutions below

0
On

The current best practice for using page objects is to keep them as decoupled as possible, and avoid chaining.

It is easier to see what is going on in the test if you are doing:

visit(LoginPage).login
on(AccountPage).add_payment
expect(on(ConfirmationPage).success?).to eq true

versus:

expect(visit(LoginPage).login.add_payment.success?). to eq true

As for the indeterminate state issue, you likely need to follow the pattern of waiting for a condition to be met (order processed) to synchronize the test with expectations and then proceed. Something like this:

order_id = OrderPage.submit_order
wait_for_processed(order_id)
visit(ProcessedOrderPage, params={order_id: order_id})