ProTips in Managing UI

Published on October 30, 2012

Hi, we’ve decided to start a pro tips post series on Codeception. We hadn’t too much time recently, because of the work on our startup Bugira, and the release of community-driven catalog oj Javascript libraries - Jster. You should definitely visit Jster (and add it to bookmarks), as it contains more then 800 javascript libraries for any needs in frontend development. But finally, We can get back to Codeception and tell about it’s best usage practices.

Tip 1: Variables for UI elements

You can write pretty complex tests using Codeception. When you have lots of them you may find that changing page UI elements can lead to refactoring all the tests. If you plan to develop a solid automation platform for your web application, consider putting all UI elements into the variables. In this case you can modify your tests pretty easy. If you see like you are creating too much tests, you should think on reorganizing them in a way that can act stable despite of changes in project. It’s really hard, features, designs, can constantly change and affect tests. But it’s possible to keep your tests stable if you use variables instead of raw values in tests.

Let’s say we have an acceptance test tests/acceptance/CreateTaskCept.php:

<?php
$I = new WebGuy($scenario);
$I->wantTo('create todo task');
$I->amOnPage('/tasks');
$I->fillField('New Task','Update a blog');
$I->click('Add');
$I->see('Update a blog', '#tasks');
?>

What if we change the name of buttons and the DOM position of the element where “Update a blog” text should appear? Let’s move this elements to a _bootstrap file and push them into variables.

In tests/acceptance/_bootstrap.php:

<?php
$task_add_button = 'Add';
$task_new_field = 'New Task';
$tasks_list = '#tasks';
?>

And in tests/acceptance/CreateTaskCept.php:

<?php
$I = new WebGuy($scenario);
$I->wantTo('create todo task');
$I->amOnPage('/tasks');
$I->fillField($task_fill_field,'Update a blog');
$I->click($task_add_button);
$I->see('Update a blog', $tasks_list);
?>

As you see replacing values with variables shouldn’t affect the test readability while you choose proper names for UI elements. We can also recommend to store user names, passwords, etc in bootstrap too. It will be automatically included on each test run. Use it.

Tip 2: Use PageObject

The PageObject is a pattern very popular among QA automation professionals. PageObject allows us to organize variables from a previous tip in more structured way. Instead of using raw variables we will group them into a class and use them in test and helpers. But classes and constants can’t be added to _bootstrap, because bootstrap will be loaded before each test, so it will trigger an error: “Fatal: Cannot redeclare class”. Let’s deal with that by creating a new PHP file for TodoTask page class:

In tests/acceptance/_todoTaskPage.php:

<?php
class TodoTaskPage {
	const URL = '/tasks';

	static $add_button = 'Add';
	static $new_field = 'New Task';
	static $list = '#tasks';	
}
?>

Let’s add it to tests/acceptance/_bootstrap.php:

<?php
require_once '_todoTaskPage.php';
?>

In this case you won’t get lost in where which UI element is located. Because in PageObject pattern the UI element is bound to a page, which we defined by the URL property. And here is the updated test:

<?php
$I = new WebGuy($scenario);
$I->wantTo('create todo task');
$I->amOnPage(TodoTaskPage::URL);
$I->fillField(TodoTaskPage::$new_field,'Update a blog');
$I->click(TodoTaskPage::$add_button);
$I->see('Update a blog', TodoTaskPage::$list);
?>

After this update we have all the UI elements for todo task page kept in one place. Using and IDE you can easily find the right element and insert it into any test of your suite. Despite the raw variables classes and constants are accessible in helpers and unit tests. Also you can improve the page object classes with PHP’s OOP power. If you are using PHP 5.4 you can include global UI elements (menus, breadcrumbs) with traits.

Lets create a trait for menu component in tests/acceptance/_menu.php:

<?php
trait GlobalMenu {
	public static $global_menu = "//div[@id=menu]";

	public static function menuItem($index)
	{
		return self::$global_menu.'/ul/['.$index.']';
	}
}
?>

We add it to tests/acceptance/_bootstrap.php:

<?php
require_once '_menu.php';
require_once '_todoTaskPage.php';
?>

And we are using it in previously declared TodoTaskPage:

<?php
class TodoTaskPage {

	use GlobalMenu;

	const URL = '/tasks';

	static $add_button = 'Add';
	static $new_field = 'New Task';
	static $list = '#tasks';	
}
?>

And now we can use properties and methods of a trait included in class. Thus, TodoTaskPage, as any class that uses GlobalMenu trait can access a menu. Let’s write a new test for checking that menu contains a button to create a task.

<?php
$I = new WebGuy($scenario);
$I->wantTo('check task creation link is in a menu');
$I->amOnPage(TodoTaskPage::URL);
$I->see('Create Task', TodoTaskPage::menuItem(3));
?>

If you think on layout components as a traits and pages as classes, you can reorganize your UI elements in a very flexible way. Changes in markup or texts won’t affect a tests, as you can easily update all the tests that rely on that element. You can even teach your template developer to update the corresponding PageObject each time she changes template.

Conclusion

Codeception is simple, yet powerful tool for test automation. If you plan to use test automation for the enterprise level projects, it’s better to start with proper organization. As we think on architecture before writing the first line of code, we should think on scalable architecture of tests platform. Using PageObject and variables can dramatically improve the reusability (and keep KISS and DRY principles) of your tests.