Once upon a time…

This post is all about testing. If you are already testing your code, you can skip it.

Imagine PHP without the in_array() function: you need it, so you have to write it:

function inArray($needle, array $haystack)
{
	foreach (...) { ... }
	return $found;
}

Then you check if it works:

$array = array(4, 8, 15, 16, 23, 42);
var_dump(inArray(2, $array));
var_dump(inArray(16, $array));

You see that everything works as expected, you throw away the three lines above and commit your code. It works, afterall!


Considerations:
- you spent some time to write a manual, use-and-throw test;
- if your code changes, for example you implement the third optional parameter strict, you have to test the old functionalities (déjà vu) and the new ones;
- you can’t make predictions about code’s immutability (and often, it changes).

LOST system failure

They could automate the execute button pushing

Use automated testing
Testing your code by hand is nice (you show that it works — at least for now), but if your computer does it for you, it is nicer. All you have to do is to explain how your software should work by writing a (reusable) test. You are slower to execute tests, you can forget to do it, you may do mistakes while doing it. A computer won’t.

Def.: A test is an executable specification for your software that you write once and execute whenever you like. In a test you setup an environment needed by the SUT (system under test), you exercise the SUT and you do some assertions on the results.

Now that you know what and why, let’s be more practical. First, install PHPUnit. Now suppose that the above inArray() definition is contained in the file InArray.php.
Create a file named InArrayTest.php with this content:

<?php
include('InArray.php');
class InArrayTest extends PHPUnit_Framework_TestCase
{
	public function testFindsExistingElements()
	{
		// setup
		$array = array(4, 8, 15, 16, 23, 42);
		// try the system
		$elementInArray = inArray(16, $array);
		// check results
		$this->assertTrue($elementInArray);
	}

	public function testWontFindUnexistingElements()
	{
		$array = array(4, 8, 15, 16, 23, 42);
		$elementInArray = inArray(16, $array);
		$this->assertFalse($elementInArray);
	}
}

You just defined the container for the tests of your inArray() function – the InArrayTest class. PHPUnit will search in it for public functions starting with test*() and will execute them. In other words, a class for each thing to test (thing is for: a class, a function, an Ext window, …), a method for each group of assertions.

In these functions (“tests”):
1) you will prepare the environment the SUT will need (for example: creating sample data, instantiating a collaborator, opening an ExtJS window of the application);
2) you will exercise your SUT (inArray, in our case);
3) you will check that everything works as expected with assertions. You can check that a variable is true (assertTrue), that a variable has a known value (assertEquals), and so on.

This is the same way you would do by hand. To test our function, we have 1) built a sample array, 2) called inArray on it and 3) printed the result and we have thought: “it returns true and I wanted it to be true.. it works”.

To run your test file, you can type:

$ phpunit --testdox InArrayTest.php
PHPUnit 3.5.0 by Sebastian Bergmann.

InArray
 [x] Finds existing elements
 [x] Wont find unexisting elements

Note: we created two tests, for two functionalities. This is ok for fast tests, but if the test setup takes seconds (for example you are creating a database with data, or you are asking Selenium to open a browser to test your ExtJS application) you should check more things in a single test.
That should be avoided when possible, but done to keep test execution time low (when you change some code, you want a fast feedback).

An important result
Now that you stuck your specifications in an executable format, you can refactor/add functionalities to your SUT being sure not to introduce regressions – if you break something, at least a test will fail.

How many time you said: “why this code? it could be simpler” or “this happens because that function is called here, but it shouldn’t happen, let’s remove the call”? And each time you fixed a bug, but you destroyed something else.

Golder rules
> Remember to test everything that is important, and skip what is not necessary. Cover all the possible cases with your tests.

> If you cover all the cases of interest, you are in a safe place. You can fix bugs without break other things, you can fix bugs on your in-production software without stress (the test suite passes? no regressions, you can deploy). In other words, with tests you will always know what works and what not.

> If you find a bug, write a test to reproduce that bug. Then make the test pass, fixing the bug. Has your supervisor ever shouted you “why this problem still occurs?” or not?

Selenium
Selenium Remote Control is a server that accepts requests like “open the page at $url“, “click on element with selector #mybutton” and it lets you see the DOM.
To execute your requests, it opens a browser (or a set of browsers, if you want), it loads your page and executes your commands. All this (almost) at the speed of light.
Download it from here and run it with java -jar selenium-server.jar.

You will give commands to Selenium via PHPUnit. To do this, you have to extend PHPUnit_Extensions_SeleniumTestCase or ExtDiamond_TestCase (which extends the first).
See this example: we are asking Selenium to open a certain URL and to check the page title. See Selenium API to check out other useful methods.

Rich Internet Applications
Testing a web site is simple –  you do some asserts on some elements. Complicate things may only be forms population and submit.
For example, you tell to Selenium to open shownews.php?newsid=1 and you assert that div#title, div#introduction and img.articlecategory elements all have some content. Done.

Testing web applications is harder. You will need to click on some elements, that will open some windows, you will interact with them and then you will do some assertions on the DOM. That is generally more complicate.

ExtDiamond aims to make this simple. See the grid ExtDiamond example. The page under test automatically renders a window with class firewall-window. If the window would not be automatically displayed, you could call

$this->click('css=div.something-that-will-open-window');

In both cases you have the opened window, at line 30 we get it’s reference. Then on line 32 we get the value of the title property (on the Ext.Window object) and we check that it’s value is correct. As alternative way, you can access the DOM of the window and assert on the title on the DOM.

Read the ExtDiamond presentation to find out which ways is better depending on your case. I also recommend you to scan PHPUnit, Selenium and ExtDiamond APIs.

… happily until their assertion failures deaths.

This entry was posted in Things you must know. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>