For testing Web Pages Codeception offers you two technologies:

Since we are currently doing some bigger refactorings of our Codeception setup, we were wondering if it is possible to use the same page object for both a WebDriver and a PhpBrowser tester. In this article I summarize our considerations.

For now our page objects were mere collections of static locators, together with some static functions to construct locators. Our main goal is to restructure these page objects in a more OO-like fashion, so that in test cases you can really use the page object to do stuff with a given page.

While so doing, we were wondering if it is possible to use the same page object for both a WebDriver and a PhpBrowser tester. Let’s first look at a basic setup with such two testers in Codeception:

When you now write a page object, say Homepage.php, and you want to do some site interaction there with a tester, you need to decide if you want to use the WebDriverTester or the PhpBrowserTester. So your page object would have an constructor like this:

 public function __construct(WebDriverTester $I)
        $this->pageTester = $I;

This is fine if the page object is only to be used by the WebDriverTester. Since the WebDriver and PhpBrowser modules share a huge part of their functions, one might also consider using the page object for the PhpBrowserTester. But this is not possible anymore, since we fixed the WebDriverTester in its constructor.

Thus, we need to make separate page objects for the WebDriverTester and the PhpBrowserTester. Which is perfectly fine, but one may want to use the same page object for both. So how could we do that?

One obvious approach would be to extract an interface or abstract class which contains all functions which are implemented by both the WebDriver and PhpBrowser modules. The nice thing is, this interface already exists:

namespace Codeception\Lib\Interfaces;
interface Web

So we could add a new tester called WebTester and extend the picture in this way:

The WebTester simply is this:

abstract class WebTester extends Actor implements Web

Unfortunately, it does not work that simple. The reason is, that when Codeception generates the Actions classes, it does not take the exact signature from the modules, but a slightly more generalized form. For instance this method from the WebDriver resp. Web interface:

 public function submitForm($selector, array $params, $button = null)

becomes in the Actions classes:

public function submitForm($selector, $params, $button = null)

As a result, the WebTester class does not implement the Web interface. One way to fix this, is to make a copy of the Web interface, and change the problematic signatures in the way they are generated by Codeception for the Actions classes. If we call this new interface WebGen, we could get this picture:

You see it is already getting a bit messy. We need to manually correct the Web interface into WebGen. But it would work like this, we could now say for our page objects:

 public function __construct(WebTester $I)
        $this->pageTester = $I;

But keep in mind, that this restricts now your WebTester to all functions which are both in WebDriver and PhpBrowser defined (i.e., the Web interface). You cannot use anymore things being specific to only one of the two.

And there is another little drawback. In a Codeception test you can use dependency injection to let it generate instances of your page objects. For instance:

public function myTest(WebDriverTester $I, Homepage $homepage)

If our page objects now uses the abstract class WebTester in its constructor (as seen above), then the dependency injection will fail since it cannot construct an object from an abstract class. So you need to construct your page objects here explicitly in the test, like:

public function myTest(WebDriverTester $I)
        $homepage = new Homepage($I);

At the end we decided to go the predefined way of having page objects being specific for a given tester. If we later have some slightly similar page objects for the two testers, we can live with that. If you have some comments or better ideas, please let us know!

No comments

Add Comment

You can use [geshi lang=lang_name [,ln={y|n}]][/geshi] tags to embed source code snippets.
Gravatar, Twitter author images supported.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.