Should assertions conditions be done in the page Objects, or in the Test classes?

One thing that still baffles me to this day is, should we implement the methods to check our assertions conditions exclusively in Page Objects, or should we do those in our Tests?

In other words, given a method:

boolean isPlayButtonDisplayed()
return driver.findElementByID(“play-btn”).isDisplayed();

(I’m erasing this as it probably isn’t a good example,
so please refer to the example below)

boolean doesDataTableCellTextEquals(int row, int column, string text)
return driver.findElementByLocator("data-table-cell-locator").getText().equals(text);

Should these methods be always implemented in the corresponding Page Object, and then call them in the assertions in our Test(s), or should we just call the driver in the Test class, implement the code/method that makes the verification in that Test class, and use it check the condition?

Is using the driver in the Tests classes considered bad practice? Should it be banned, as it is not promoting code re-usability?

4 Likes

Personally, I always favor assertions in the test, to make it as obvious as possible when reading the test what is really being tested.

11 Likes

I think of it in terms of the purpose of each thing. For me:

  • Page Object - make the page easier to interact with
  • Test - manage the test, possibly delegating to other things such as the Page Object.

So the assertions are the job of the test. The Page Object shouldn’t know it’s part of a test at all - it just provides a way to interact with a page, and this happens to be used as part of a test.

The jobs delegated to it by the test are: read information from the page, send information to the page (which could be filling stuff in on a form, but could also be things like clicking on controls to show/hide other controls - the information is “I want you to hide this thing”)

2 Likes

I would find it weird to have a test that checked if a button is displayed. I might have a test that clicks on it to make sure it plays though - which would fail if it’s not displayed.

I limit tests to only remarking upon explicit checks that are part of the user story. For example, if there’s a message that needs to appear on an element it should “expect message on element” and not check anything else.

I once got asked to check for broken links. I considered this an implicit check, so I never included it in any test. Instead, I implemented a chunk of code after page load that cycled through all of the links on the page and checked to see if any of them 404ed. It would then be built into any new page load.

(I then only ran it at night because holy hell that check slowed things down!)

2 Likes

So you leave the Page Objects exclusively for navigation, and you write all the code that goes into an assertion in the Test Class?

2 Likes

I understand that assertions should be done in Test classes, my question was more towards where should we put the code that will fetch the result that we’re putting into the assertion.

1 Like

Sorry about the example I gave in my post, it really isn’t the best fit.

Perhaps I could edit it to something like this :point_down:

boolean doesDataTableCellTextEquals(int row, int column, string text)
return driver.findElementByID("data table cell locator").getText().equals(text);

So then we call this method (that is implemented in a Page Object) inside an assertion, in our Test class?

What I was trying to know is if the code that checks a given condition should be left in a Page Object for it to be reused by other Tests, or should we just put it in that Test class?

I’ve read that we shouldn’t make use of the WebDriver in the Test Classes (expect for passing it as an argument for our PageObjects).

I don’t think it matters. The important thing is that the test class describes a high level readable “story-relevant” check that will make sense to the test reader and that the implementation details are pushed down to the next layer.

As far as I’m concerned, a method that returns bool that you assert on is equally readable to a method that does the assert itself.

In general I’d probably default to putting the assertion in the page object though, and implicit checks like my “do any links 404?” should always be in there.

I understand now what you’re querying, and I agree with you.

I think it’s valid to have a method in a page object that asks “Does X equal Y?” where X is from the page and Y is passed in. You can then have one test that checks
assert(doesTableEqualText(textToCheckFor), equals(True))

and you could have another test that uses the same method differently e.g.
assert(doesTableEqualText(otherTextToCheckFor), equals(False))

because the first test wants to check that the page’s table is the same as the searched for text, and the other test wants the page’s table to be anything other than the given text.

1 Like

I do multiple things, basically trying to stay DRY (Don’t repeat yourself - so I do not have multiple places to refactor or fix):

  • very basic and reoccurring actions and checks related to that page I do either in the Page Object class or in a utility class named similar.
  • for more complex reoccurring actions and checks, sometimes covering multiple pages, I create dedicated utility classes. E.g. one for login.
  • in the test class I call this other methods and add just what is unique to it.

e.g. I need to check a drop-down menu having different content at different test cases. Then I develop a utility method at a common place which does all the necessary interaction with component(s) and have as parameter only the list of entries to check for.
(sometimes it needs multiple actions on a page to get to that drop-down menu. e.g. opening “closed” elements)

Know your code, give it good structure and good names to methods.
Then you can “hide” many things behind methods.

I’m here not a friend for “black vs white” and academic fights which approach is better. It is grey at best and you need sometimes this approach and sometimes that approach.
Every problem needs an context depended solution.

2 Likes

Interesting conversation, it made me check my own understanding once more. What it comes down to for me, after also reading the various responses, is:

In the neatest design I can think of:

  • A page object represents (part of) a page. Its methods are the logical actions on that page, such as logging in in case of a login page (using username and password). It normally only supports physical actions such as clicking the login button through these logical actions, not separately. One important consequence is that when physical details of the page change but the logical action remains the same, the changes will usually be limited to the page object and not affect the tests that use that page object.
  • Explicit assertions are done in the test. This can be done by performing the whole check for the assertion in the page object, returning a boolean, and asserting based on that result. This is unwise if it means that actual test logic ends up in the page object, since this is not part of the behavior of the page that it represents but of the test: separation of concerns. It can also result in multiple methods that perform different checks on the same element - code duplication. It is also possible to retrieve information from the page object through a getter method and then performing the comparison and assertion in the test. This can make the test easier to understand (and can reduce code duplication in the page object). It is best not to share tool objects (such as the WebDriver object or a WebElement) with the test, but only POJOs such as strings, numbers and custom classes. This is separation of concerns again and also reduces maintenance effort.
  • Implicit assertions such as always checking for any broken links should be performed under the hood in the page object. It is not only the place that should have knowledge of any links and other things relating to the physical page but also avoids a lot of duplication and other issues.

What did I miss?

For interaction, maybe beyond also navigation, but I wouldn’t but an assert into the page object. Not least because, page objects are reusable, and each test might want to test some different thing.

I need my test to control what a failed action means, it might be the test isn’t failed, it’s blocked or some other state that means the it failed before I could assert the behaviour the test covers.

1 Like

Ohhh like a comparator / assertion assistance function in the page object. This makes more sense, and should still read semantically well from the test, so I would be OK with this.

1 Like

This is part of what I was trying to explain with my example.

The individual expected values should come (mostly :slight_smile: ) from the test case itself.
But a “generic” (parameterized) assertion method, especial when being more complex, can be implemented elsewhere.

Why the “mostly”?
Because I sometimes have the case that some test cases have to check the same values at the same places (in addition to individual ones). Why should I then duplicate (complex) code?

And yes, I also do not always do DRY.
At some places its better to repeat yourself. e.g. for readability.
Even DRY is just a tool, not law.

1 Like

After a couple refactorings, we have landed on using Selenide and have most of our assertions in the Page Object. So our Test class has tests like:

    @Test(priority = 3)
    public void validateKnowledgeBase(){
        pageObject.knowledgeBaseTileValidation();
        pageObject.knowledgeBaseNav();
    }

and our Page Object methods like:

    public void knowledgeBaseNav() {
        $("#TrainingVideos").shouldBe(Condition.enabled).click();
        // etc.
    }
2 Likes

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)

1 Like

This is very similar to how our GUI tests are structured, where the test is simply a sequence method calls that act like test steps to follow. Most, if not all assertions and logic are in the Page object. I’ve always felt this makes the test cleaner, and as long as the methods are named appropriately, the intentions of the test should be clear.

To facilitate DRY principles where possible, we use Utility classes, as has been mentioned above.

It’s probably not the best OO programming or strictly the correct use of POM, but it’s always worked for me. :+1:

2 Likes

I think you are talking about “chaining” of method calls in a page-object @brgibb , where each action returns an object that exposes the next action? I love it when we can get this to work nicely too. It should be a goal in every testers life to build tests that look like a natural language flowing along a page (sic).

2 Likes

That means you have to expose the testing framework (e.g. TestNG, jUnit, etc… ) to the main code, so you can use assertions in Page Objects for instance, right?.
For some (hopefully good) reason those frameworks default’s scope are set to the code that’s in <scope>test</scope>.

There is some good material on page objects, including the aspect of asserting, to be found with the Selenium documentation:

1 Like