welcome to the club @jgfmachado . wow . another good question.
I think, and this is just my experience from the last 3 years, that we are seeing 2 things emerge here, perhaps 3, the old add-age, there are only 2 types of programming errors, cache consistency and naming of the animals; oh and off-by-one-errors!
I prefer to assert in a test. Page objects should only assert when they are unable to do anything logical next. So any ânavigationâ or âclickâ method may assert, a âconstructionâ of page implicitly asserts, and any other âactionsâ and functions that return another page-object or element should also assert. Anything that returns a value normally would not want to assert, except in cases where the value is unobtainable altogether.
Why? If too many methods in a class/object assert, then it makes it too messy to build negative test cases, and it makes any exploration test writing more painful too, so avoid.
However it becomes very context sensitive when your assertion is needing to be done all of the time anyway. At that point, consider whether the name of the function you called is perhaps wrong.
As @hitchdev points out, tests should not be checking or even waiting to see if a button or control is present, thatâs a big âcouplingâ problem waiting to happen. The page-object should be doing that, itâs really a âdriverâ only in my way of thinking about things. Add a wait-for-control() method to the page-object if you have to.
Convenience If you find an assertion is used a lot, then add it to the page-object as an extra convenience function, but you can already see what is happening here canât you, you are breaking the OO encapsulation and single-function of the page-object by adding code that has nothing to do with the âpageâ anymore, and your page-object is becoming a âlogic-objectâ, itâs a slippery slope. So you might want to build a child class that adds helpers in order to keep that separation between page-driver and test-business-logic. You might find that this extra logic can be implemented using metaprogramming/decorators depending on your test script language, or a mixin. This will allow non-tests to also use your page-object if they want to. By keeping functions that have very little to do with the âdrivingâ in a separate class, it makes things easier to find and easier to de-duplicate things like that function for asserting lists are a certain content, by just putting that function into a mixin.
parts or wizards : @martingijsen points to the page-object being a part of a page - and I like that argument, it helped me a lot when it came to pages that flow from one to the other, or pages that are really just panels and helped me work out how to chain page objects. I opted to implement each page in a wizard as a new object/class in the end. Once again will depend on your context. When you hit a case for composing multiple page-objects, into one object, you might find you need to change your page-object inheritance to allow you to group a few classes as one class. For example you might have a page-object for your menu, and another for a dialog. but a test might want to control just one object. If this becomes your context, look at how you construct page objects so you can mixin or inherit a page from another page cleanly. This will prevent DRY errors later.
(a mixin is a python term for multiple inheritance interface/implementation base classes)