A small and simple PHP testing tool in one file.
This reference lists everything you will stumble upon in your daily work with Shinpuru. The information is extracted out of the documentation comments of the source file and the examples are taken from the actual tests. Fell free to look around and discover new things or use the browser search (strg + f) if you are in a hurry.
If you are new to Shinpuru or look for an introduction into a specific feature you might find more useful information on the project page.
Every Shinpuru test supports the following command line arguments:
-a
, --autotest
This option activates autotest mode. The test will be run once and after that the current directory
(and its subdirectories) will be monitored for changes. As soon as a changed file is saved the test
will be run again. If you work with Ubuntu Linux a confirmation bubble will show the result of the
test run. Use the --silent
option to disable this. If you want Shinpuru to monitor other directories
than the current one use the --monitor
option to specify one or more directories you want to be
monitored for changes.
-s
, --silent
This option prevents Shinpuru from triggering confirmation bubbles when run in autotest mode.
-m PATH
, --monitor PATH
Monitors PATH
for changes. If PATH
is a file this file will be monitored. If it's a directory all files
in it and its subdirectories will be monitored. As soon as a monitored file changes the test will be run
again. You can use this option multiple times to make Shinpuru monitor multiple paths. --monitor
also enables the autotest mode so you don't need to specify --autotest
once you specified --monitor
.
In fact --autotest
is more like a shortcut for --monitor .
(the dot is the current directory).
-r STATUS
, --report STATUS
Makes Shinpuru print a report which lists all tests with the status STATUS
. Valid states are failed
(the default), passed
, skipped
and successful
. You can specify multiple states by using multiple
--report
options, e.g. --report failed --report passed
. This will report all failed and passed tests.
-e ENV
, --environment ENV
Runs the test in the environment ENV
. The code after skip_in()
calls for ENV
will be skipped
and the code after only_in()
calls for ENV
will be run. This allows you to write test cases that don't
run destructive tests in a production environment. You can specify multiple environments by using
multiple --environment
options to further fine tune what test cases should be run (e.g. on a feature by
feature basis).
Since Shinpuru is used from the command line the exit codes are of some interest:
0
1
- 245
246
255
You can use this for example to run a test suite as a cron job and send a notification mail only if a test failed.
Options change some details in how Shinpuru or some of its functions work. For example you can set
a common base URL that is used by all HTTP request functions instead of writing it into every function
call. Options can be set using the option()
method and be read as properties of the test suite returned
by the suite()
function. These options are currently available:
base_url
This string will be prepended to any URL requested by the HTTP request functions (get()
, post()
,
…). This will spare you the work to write the full URL at each function call. Combined with environments
you can set different base URLs depending on the environment the test suite runs in. The default is an
empty string.
fail_on_error_level
The error level at which a test of this test suite fails. The default value is set to E_ALL
, that is as
soon as a test generates an error it fails. The reasoning behind this rather strict default value is that
even notices (E_NOTICE
level errors) often indicate errors in the code. If this value is to strict you
can use this option to set it to a more permissive value (e.g. E_ALL ^ E_NOTICE
).
This is more of a cosmetic option but with the name()
function you can set the name of a test suite.
The default name is the base name of the test suites file.
function option($name, $value, $target_environment = null)
Sets the option $name
of this test suite to the specified $value
. All valid option names are listed
above. If you specify the optional target envionment the option is only set if the test suite is run in this
environment.
$name
$value
$target_environment
The value of an option can be read as a property on the test suite (returned by the suite()
function).
<?php
require('shinpuru.php');
// Sets the "base_url" option to "http://localhost". This makes all HTTP request
// functions relative to localhost.
option('base_url', 'http://localhost');
// Overwrites the base URL to arkanis.de if the test suite is run in the production
// environment.
option('base_url', 'http://arkanis.de', 'production');
test('something', function(){
// Reads the "base_url" option of the test suite.
$current_base_url = suite()->base_url;
assert_equal($current_base_url, 'http://localhost');
});
?>
function name($test_suite_name)
Sets the name of this test suite.
$test_suite_name
option
method).The name of the test suite is available through the name
property of test suite (use the suite()
function to get it).
The basic building blocks of a Shinpuru test suite are tests which are defined with the test()
function.
However managing a test suite with dozens of tests can get a bit difficult if you don't have means to
express any structure. Therefore Shinpuru (inspired by Shoulda) provides a way to group related
tests into a "context".
A context is defined with the context()
function and can contain as many tests as you want. A context
can also contain other contexts allowing a hierarchical structure. If you have some code that is common
to all tests in a context you can put this code into setup()
and teardown()
functions. These are run
before each test in the context and it's child contexts.
function test($description, $test_function = null)
Defines a new test.
$description
$test_function
If no test function is specified the defined test automatically calls pass()
to mark itself as "not done
yet". This is especially useful for quickly dumping out a sort of specification to get an overview of
what you want the test suite to look like.
<?php
require('shinpuru.php');
test('The front page should contain 10 articles', function(){
// Code to prove this, e.g. with get() and assert_select().
});
// These tests will be marked as "passed" because no test function
// is specified.
test('The projects page should list all projects');
test('The tag cloud should be cloudy');
?>
function pass($message = 'Passed')
With a call to this function a test is marked as "not done yet" (inspired by Python). Passed tests are marked on the report so you know what's still to do.
$message
You can use the command line argument --report passed
to get a detailed list of all tests that are
not implemented yet. The $message
parameter is also listed in this report.
function context($name, $test_definitions)
Defines a new context to group related tests together.
$name
$test_definitions
If all tests in a context share some common code you can put this code into a setup()
or
teardown()
function of the context. These are run before and after each test of the context and
it's child contexts.
Contexts and tests allow you to quickly write down the basic structure of a test suite. Even without any test code the test suite is already runnable. You can then implement test after test.
<?php
require('shinpuru.php');
context('My websites', function(){
context('layout', function(){
test('should have a navigation area with links');
test('should have a non empty tag cloud');
test('should show my latest projects');
});
context('fontpage', function(){
test('should show the 10 latest posts');
});
});
context('A post', function(){
test('can have comments');
context('can be formattet with', function(){
test('HTML');
test('Markdown');
test('smilies');
});
});
?>
function setup($setup_function)
Registers $setup_function
to be executed before each test of the current context. If a context
contains multiple setup functions they are run in the order they were registered.
$setup_function
A setup function is ideal for stuff that each test of the current contest does before actually testing what it should. If you're testing a website that requires login the login code could be moved into a setup function if it's required by multiple tests.
Child context iherit the setup functions of their parent contexts. That is before any test the setup functions of all parent contexts and the current context are run.
The global scope is an context of it's own. Therefore setup()
can also be used in the global
scope and is not required to always be inside of a context()
function.
<?php
require('shinpuru.php');
option('base_url', 'http://arkanis.de');
context('The frontpage', function(){
setup(function(){
get('/');
});
test('should show the 10 latest posts', function(){
// This will test the response body of the request done in the setup function
assert_select('article', 10);
});
test('should have a main navigation', function(){
// Will also test the response body from the setup function
assert_select('nav');
});
});
?>
function teardown($teardown_function)
Registers $teardown_function
to be executed after each test of the current context. If a context
contains multiple teardown functions they are run in reverse order of their registration. This allows
you to group related setup and teardown functions together.
The purpose of a teardown function is to clean up and free the resources a setup function allocated. Teardown functions are therefore run even if the test failed and must not contain assertions (they will not be catched and PHP will report them as exceptions). If a setup function fails the teardown functions up to the context where the setup failed will be run.
$teardown_function
A teardown function can also be used on the global scope (just like setup()
can be too). Child
contexts will also inherit the teardown functions of all their parent contexts.
<?php
require('shinpuru.php');
context('The Next Big Thing', function(){
setup(function(){
// Log into your secret Next Big Thing™ webapp
});
teardown(function(){
// Log out or clean stuff up
});
test('should solve all problems', function(){
// Code to prove this
});
});
?>
function suite()
Just returns the global test suite object. This allows easy access to it's properties, e.g. the request body of the last HTTP request.
Environments allow you to define which test (that is one test()
function) should be run or skipped
under which conditions. For example you can write a test suite that tests everything when run
normally but skips several dangerous tests if run in the "production" environment (you don't want
to reset your database in a production environment).
This can be achieved by calling skip_in()
or only_in()
directly at the start of a test.
<?php
require('shinpuru.php');
test('rebuild the database', function(){
// Code after that line will be skipped in the production environment
skip_in('production');
// Imagine the god of destruction who rebuilds everything after he's done…
});
?>
If this test suite is run in the "production" environment (with the --environment
or -e
command line
argument) the test is marked as skipped:
$ php test_dummies/environments/skip_with_one_env.php --environment production Running: skip_with_one_env.php s 1 test skipped No tests failed, good job!
skip_in()
as well as only_in()
can take an array of environments as the first parameter and a test
suite can also take several environments as arguments. This way you can write very fine adjustable
tests (e.g. on a feature by feature basis).
function skip_in($environment, $reason = 'Skipped')
Skips everything of the test after this function. This is meant to mark some test as not relevant or unsuitable for a specific environment. For example you don't want to do destructive tests in your production environment but you can still test read only stuff.
With skip_in()
you would mark those destructive tests with skip_in('production')
and run the
test suite on the server in the production environment (--environment production
). If this is to
dangerous you can do it the other way around and use only_in()
to run destructive tests only
in your testing environment.
Skipped tests are shown in the report so you have an impression how much was successfully tested and how much was skipped.
$environment
$reason
If you run the test suite with the command line argument --report skipped
all skipped tests are
listed with their corresponding reasons.
test('rebuild the database', function(){
// Code after that line will be skipped in the production environment
skip_in('production');
// Imagine the god of destruction who rebuilds everything after he's done…
});
test('rebuild the database', function(){
// Code after that line will be skipped in the production and fast-test environments
skip_in(array('production', 'fast-test'));
});
test('rebuild the database', function(){
// In the production environment code after that line is skipped with the
// message "it would erase all customer data (bad for the job…)"
skip_in('production', 'it would erase all customer data (bad for the job…)');
});
function only_in($environment, $reason = 'Skipped')
This is the negative skip_in()
, that is it only continues if the test suite is run in the specified
environment. It also accepts an array with multiple environment names as the first parameter.
$environment
$reason
If you run the test suite with the command line argument --report skipped
all skipped tests are
listed with their corresponding reasons.
test('test dead links', function(){
// Code after this line is only run in the production environment
only_in('production');
});
test('test dead links', function(){
// Code after this line is only run in the production and deep-test environments
only_in(array('production', 'deep-test'));
});
test('test dead links', function(){
// If the test is not run in the production environment code after that line is skipped
// with the message "during development links are constantly broken".
only_in('production', 'during development links are constantly broken');
});
With the following assertions you can cover the functionality of basic PHP code:
true
, false
, null
and not null
).You can also use PHPs build in assert()
but note that you can not specify a custom error message
for it. In that case you need to use assert_true()
.
function assert_equal($value, $expected_value, $message = 'Got value %s but expected %s')
Verifies that $value
equals $expected_value
. For that the usual PHP equality operator is used
(==).
// will pass assert_equal(1, 1); // will pass assert_equal('test', 'test'); // will pass as PHP considers false == '' assert_equal(false, ''); // Fails… otherwise it would be hard to breath assert_equal('earth', 'moon'); // Fails with the message "1 is not 2!" assert_equal(1, 2, '%s is not %s!');
function assert_not_equal($value, $forbidden_value, $message = 'Got %s with is equal to the forbidden value of %s')
Verifies that $value
is not equal to $forbidden_value
. For that the usual PHP equality operator
is used (==).
// All three will pass assert_not_equal(1, 2); assert_not_equal('mars', 'jupiter'); assert_not_equal(false, true); // Fails because PHP considers false to be equal to '' (an empty string) assert_not_equal(false, ''); // Fails with the message "%s is equal to %s but should not be!" assert_not_equal(false, 0, '%s is equal to %s but should not be!');
function assert_true($result, $message = 'Expected true but got %s')
Verifies that $result
is true
.
This assertion is more or less like PHPs build in assert()
but supports an optional message in case
the assertion fails.
// will pass assert_true(1 == 1); // fails with "15 isn't in 1..10, is it?" assert_true(in_array(15, range(1, 10)), "15 isn't in 1..10, is it?");
function assert_false($result, $message = 'Expected false but got %s')
Verifies that $result
is equal to false
.
Note that in PHP several values are equal to false
, e.g. 0
and ""
(an empty string). If you want
to verify that something is identical to false
use assert_identical()
.
// all these will pass assert_false(1 == 2); assert_false(false); assert_false(0); assert_false(''); // Will fails since 1 == 1 is true assert_false(1 == 1); // Fails with the message "Sorry, 1 is always equal to 1" assert_false(1 == 1, "Sorry, 1 is always equal to 1");
function assert_null($result, $message = 'Expected null but got %s')
Verifies that $result
is identical to null
.
// will pass assert_null(null); // Fails because false is not null assert_null(false); // Fails with "Sorry, 0 is not null" assert_null(0, 'Sorry, 0 is not null');
function assert_not_null($result, $message = 'Expected something else than null but got %s')
Verifies that $result
is not null
.
// all these will pass assert_not_null(0); assert_not_null(''); assert_not_null(false); assert_not_null(true); assert_not_null(null); // Fails with "A message stating why null is null" assert_not_null(null, 'A message stating why null is null');
function assert_identical($value, $expected_value, $message = 'Got value %s but expected something identical to %s')
This is assert_equals()
bigger sister (bigger sisters are always strict, you know… no offense
intended). Anyway, assert_identical()
verifies that $value
is identical to $expected_value
as with PHPs ===
operator. That is both parameters are expected to have the same value
and type.
// Tese two will pass assert_identical(1, 1); assert_identical('mars', 'mars'); // Fails because 1 is not equal to 2 assert_identical(1, 2); // Fails with "The planet mars is not jupiter" assert_identical('mars', 'jupiter', 'The planet %s is not %s'); // Both will fail because they have not the same type (but are still considered equal by PHP) assert_identical(false, 0); assert_identical(false, '');
function assert_not_identical($value, $forbidden_value, $message = 'Got the forbidden value %s')
Verifies that $value
is not identical to $forbidden_value
.
// Both will pass assert_not_identical(true, false); assert_not_identical(1, 2); // Will fail since null is identical itself assert_not_identical(null, null); // Will fail with "Got 10 but wanted something not identical to 10" assert_not_identical(10, 10, 'Got %s but wanted something not identical to %s');
function assert_empty($value, $message = 'Expected something empty but got %s')
Verifies that the specified parameter is empty. That is empty($value)
returns true
.
// Both will pass assert_empty(array()); assert_empty(''); // Fails because the array is not empty assert_empty(array(1)); // Fails with "abc is not empty!" assert_empty('abc', '%s is not empty!');
function assert_not_empty($value, $message = 'Exptected something that is not empty but got %s')
Verifies that the specified parameter is not empty. That is empty($value)
returns false
.
// Both will pass assert_not_empty(array(1)); assert_not_empty('abc'); // Fails because the array is empty assert_not_empty(array()); // Fails with "0 is empty!" assert_not_empty(0, '%s is empty!');
function assert_count($something_countable, $expteced_count, $message = 'Got a count of %s but expected %s')
Verifies the count of $something_countable
to be qual to $expteced_count
.
// All four will pass assert_count(array(1, 2, 3), 3); assert_count(array(), 0); assert_count('abc', 1); assert_count('', 1); // Will pass because count() returns 0 for undefined variables assert_count(null, 0); // Will fail since the array contains 2 elements instead of 4 assert_count(array(1, 2), 4); // Will fail with "Got 3 elements but wanted 1!" assert_count(array(1, 2, 3), 1, 'Got %s elements but wanted %s!');
function assert_contains($haystack, $needle, $message = 'Could not find %s in %s')
Verifies that $haystack
contains $needle
. If $haystack
is a string strpos()
will be used, if
it's an array in_array()
will be used for the check.
// Will pass since strpos() is used to check if "world" is present in the "hello world" assert_contains('hello world', 'world'); // Will pass since in_array() is used to check if 2 is contained in the array assert_contains(array(1, 2, 3), 2); // Will fail since 4 is not in the array assert_contains(array(1, 2, 3), 4); // Will fail with "moon does not occur in hello world" assert_contains('hello world', 'moon', '%s does not occur in %s');
function assert_not_contains($haystack, $needle, $message = '%s was not supposed to be in %s')
The exact oposit of assert_contains()
. Fails if the $needle
is part of $haystack
.
// Both will pass assert_not_contains('hello world', 'moon'); assert_not_contains(array(1, 2, 3), 4); // Fails with "Found world in hello world! This should not happen!" assert_not_contains('hello world', 'world', 'Found %s in %s! This should not happen!'); // Fails since 2 is part of the array assert_not_contains(array(1, 2, 3), 2);
function assert_regex($pattern, $haystack, $message = '%s does not match %s')
Verifies that the regular expression $pattern
can be found in $haystack
. preg_match()
is used to do the job.
// Both pass since both patterns occur in the specified string assert_regex('/a+/', 'baa'); assert_regex('/(\w+\s?)+/', 'bla hallo bla'); // Fails since no "a" can be found in the string assert_regex('/a+/', 'bbb'); // Fails with "The pattern /[0-9]+/ could not be found in hello world" assert_regex('/[0-9]+/', 'hello world', 'The pattern %s could not be found in %s');
These functions make it easy to perform HTTP requests. PHPs file_get_contents()
function is used
so you have the full power of PHPs streams at you disposal. You can modify the behavior of every request
function by specifying context options which allow you to add you own headers (e.g. for authorization),
change the user agent, set SSL options, etc. In fact post()
, put()
and delete()
are nothing more
than calls to get()
with some context options set.
function get($url, $context_options = array())
Performes an HTTP GET
request on the specified $url
and returns the response body. If the base_url
option is set it's value will be prepended to the URL. get()
will fail if the HTTP request was not
successful (e.g. DNS lookup failed or an 404 error code was returned). If you want to ignore HTTP error
codes you can set the ignore_errors
HTTP context option. After the request the HTTP assertions
can then be used to verify several aspects of the HTTP response (response code, content).
$url
The URL to request, e.g. http://arkanis.de/projects
. If the base_url
option is set it's value will be
prepended to the URL. For example you can set the base_url
option to http://arkanis.de
and then
request the URL /projects
. Together this will request http://arkanis.de/projects
, too.
You can specify any URL PHPs file_get_contents()
function can handle, e.g. FTP or HTTPS will work,
too.
$context_options
stream_context_create()
. This allows you
customize the request (e.g. setting additional headers, the request method, the user agent string, SSL
behaviour, …). Especially the HTTP context options are worth a look.false
is returned.
The details of the response are available as properties though the test suite:suite()->response_body
contains the response body as a stringsuite()->response_status
is the HTTP status code (e.g. 200 or 404)suite()->response_status_phrase
contains the HTTP status phrase (e.g. "OK" or "Not Found")suite()->response_headers
is an array of HTTP headers with the header names as keys and their
content as values// The "base_url" option is set to a host where this page really exists // e.g. option('base_url', 'http://localhost'); $response = get('/html_test_page.html'); assert_response(200); // You can access the details of the last response of the test suite and verify // some details yourself. assert_equal(suite()->response_body, $response); assert_equal(suite()->response_status, 200); assert_equal(suite()->response_status_phrase, 'OK'); assert_equal(suite()->response_headers['Content-Type'], 'text/html'); // If you don't want get() to fail on HTTP error status codes you have to // set the "ignore_errors" context option. get('/nirvana.html', array('http' => array('ignore_errors' => true))); assert_response(404); // Fails since file_get_contents() will trigger an error for the HTTP 404 // status code if the "ignore_error" context option is not set. get('/nirvana.html');
function post($url, $data = array(), $context_options = array())
Sends an HTTP POST
request with the specified $data
to the $url
. It's basically the same
as get()
but with some context options set to make it a POST
request.
$url
POST
request to. If the base_url
option is set it's value will be
prepended to the URL.$data
http_build_query()
is used
to encode the data which is then send as the request content.$context_options
get()
.false
. More details of the
response can be accessed as properties of the test suite, see get()
.// Sends a POST request to http_test_page.php just like a ordinary HTML form post('/http_test_page.php', array('name' => 'Anonymous', 'content' => 'hello world!'));
function put($url, $data = array(), $context_options = array())
Sends an HTTP PUT
request with the specified $data
to the $url
. It's basically the same
as get()
but with some context options set to make it a PUT
request.
$url
PUT
request to. If the base_url
option is set it's value will be
prepended to the URL.$data
http_build_query()
is used
to encode the data which is then send as the request content.$context_options
get()
.false
. More details of the
response can be accessed as properties of the test suite, see get()
.// Sends a PUT request with new data to http_test_page.php put('/http_test_page.php', array('name' => 'Anonymous', 'content' => 'hello world!'));
function delete($url, $context_options = array())
Sends an HTTP DELETE
request with the specified $data
to the $url
. It's basically the same
as get()
but with some context options set to make it a DELETE
request.
$url
DELETE
request to. If the base_url
option is set it's value will be
prepended to the URL.$data
http_build_query()
is used
to encode the data which is then send as the request content.$context_options
get()
.false
. More details of the
response can be accessed as properties of the test suite, see get()
.// Sends a DELETE request to http_test_page.php delete('/http_test_page.php');
These assertions can be used to verify several aspects of the last HTTP request. You can check the
HTTP response code or phrase with assert_response()
and check the content of the request body
with assert_select()
and assert_xpath()
.
Additional to the HTTP assertions you can also get the details of the last HTTP response and verify them with the other assertions:
suite()->response_body
contains the response body as a stringsuite()->response_status
is the HTTP status code (e.g. 200 or 404)suite()->response_status_phrase
contains the HTTP status phrase (e.g. "OK" or "Not Found")suite()->response_headers
is an array of HTTP headers with the header names as keys and their
content as valuesfunction assert_response($status, $message = 'Got HTTP status code %s but expected %s')
Verifies the HTTP resonse status code or phrase. If $status
is an integer it will verify the HTTP status
code (e.g. 200
or 404
). If it's a string it verifies the response status phrase. Note that these status
phrases are not standardized and depend on the webserver.
get('/http_test_page.php'); // Verifies the response code of 200 assert_response(200); // Verifies the response status phrase of "OK" assert_response('OK'); get('/some_not_existing_page.html', array('http' => array('ignore_errors' => true))); // Checks the 404 response code assert_response(404); // Checks the "Not Found" response status phrase assert_response('Not Found'); get('/http_test_page.php'); // Fails since the response code is 200 assert_response(404); get('/some_not_existing_page.html', array('http' => array('ignore_errors' => true))); // Fails since the response code is 404 assert_response(200);
function assert_xpath($expression, $expected = true, $message = "Got %s but expected %s on XPath test '%s'")
Evaluates the specified XPath $expression
against the latest response body and compares it
to an $expected
value. If the expression yields a list of elements you can loop over these by
specifying a function as $expected
value and check every one in detail.
For simple standard stuff you should check out assert_select()
. It converts simple CSS
selectors to XPath and then calls assert_xpath()
.
When you nest assert_xpath()
calls they are automatically scoped to the current element.
You can find further information in the DOMXPath evaluate() method, the DOMElement class and the XPath specification (the core function library is very handy).
Some handy starting points for XPath:
id("some-id")
gets the element with the ID some-id
string(/some/where/p)
to get the content of all child text nodes (concatenated) as one stringcount(//p)
to get the count of all paragraphs// Checks that there is exactly one title element in the HTML header assert_xpath('count(/html/head/title) = 1'); // Same as above but checks by specifying an expected value assert_xpath('count(/html/head/title)', 1); // Checks the content of the element with the ID "text". It contains a nested HTML element // and the string() XPath function concatenates the content of all child elements into one // string. assert_xpath('string(id("text")) = \'Some "nested" text\''); // Same as before but this thime with an expected value that saves some escaping assert_xpath('string(id("text"))', 'Some "nested" text'); // Iterates over all elements of the ordered list and verifies that each one has a class // attribute assert_xpath('count(//ol/li)', 2); assert_xpath('//ol/li', function($element){ assert_true($element->hasAttribute('class')); }); // The first assert_xpath() extracts the element with the ID "text" and the nested // assert_xpath() verifies that the found elements contain exactly one em element. assert_xpath('id("text")', function(){ assert_xpath('count(em)', 1); }); // Fails since there is only one title element in the header assert_xpath('count(/html/head/title) = 2'); // A list of nodes is returned but true was expected, therefore it fails. assert_xpath('//ul/li');
function assert_select($simple_css_selector, $test = null, $message = "Got %s but expected %s on CSS selector test '%s'")
With assert_select()
you can use simple CSS selectors to check the structure of the latest HTTP response
body. It's sort of the little brother of assert_xpath()
since all CSS selectors are transformed into XPath
expressions and then handed of to assert_xpath()
. Depending on the $test
parameter you can verify
different aspects (e.g. count of element, text content of an element, …).
$simple_css_selector
A simple CSS selector that will be translated into an XPath expression.
The mapping looks like this:
elem
→ //elem
elem#idxyz
→ //elem[@id='idxyz']
#idxyz
→ //*[@id='idxyz']
elem.classxyz
→ //elem[contains(concat(' ', normalize-space(@class), ' '), ' classxyz ')]
.classxyz
→ //*[contains(concat(' ', normalize-space(@class), ' '), ' classxyz ')]
selector, selector
→ xpath | xpath
> piece
→ / insead of //
$test
assert_select()
performs different tests.null
the expression have to yield at least
one element: count(…) > 0
.count(…) = $test
.string(…) = '$test'
.$message
The CSS class to XPath expression was taken from Parsing XML documents with CSS selectors, so all credit for it belongs to Fabien Potencier.
// Checks if at least one title element exists: count(…) > 0 assert_select('title'); // Verifies that there are three list item elements in the ul elements: count(…) = 3 assert_select('ul > li', 3); // Tests that the text content of the selectors matches the expected text assert_select('p#text', 'Some "nested" text'); assert_select('body > p#text', 'Some "nested" text'); // Verifies the string value by using class selectors assert_select('.numbers > li.first', 'one'); assert_select('ol.numbers > *.last', 'two'); // Checks that there are three list items in the ul element by using an iterator and // the checkpoint assertion. assert_checkpoints(3, function(){ assert_select('ul > li', function($element){ checkpoint(); }); }); // Fails because there is just one title element assert_select('title', 2); // Fails with "Got Some \"nested\" text but expected wrong text on CSS selector test 'p#text'" assert_select('p#text', 'wrong text');
These assertions allow you to verify some aspects of your programs control flow like checkponts and exception handling.
function assert_checkpoints($expected_count, $function_or_message, $function = null)
Verifies that within the specified function $expected_count
checkpoints are
reached. That is checkpoint()
is called $expected_count
times.
// Checks for 10 checkpoints which are all reached and therefore passes assert_checkpoints(10, function(){ for($i = 0; $i < 10; $i++) checkpoint(); }); // Fails with "Message: reached 1 of 2 checkpoints" assert_checkpoints(2, 'Message: reached %s of %s checkpoints', function(){ checkpoint(); });
function checkpoint()
Marks one checkpoint as reached within a assert_checkpoints()
function.
function assert_fails($message_or_function, $function = null)
Checks that the enclosed code contains a failing assertition.
// Is successful since the enclosed code failes assert_fails(function(){ assert_equal(1, 2); }); // Is successful since the enclosed code failes but if it would not fail // the error message "Something is horribly wrong here…" would be // shown assert_fails('Something is horribly wrong here…', function(){ assert_equal('P', 'NP'); }); // Fails since no failing code is in there assert_fails(function(){ assert_equal(1, 1); }); // Fails with the message "It didn't explode!" assert_fails("It didn't explode!", function(){ assert_equal(1, 1); });
function assert_fails_with($expected_exception_message, $message_or_function, $function = null)
Verifies that the enclosed code fails with the specified exception message.
Note that this assertion strips all console control codes from the error message before comparing it with the expected message. This way the console formating does not get in our way.
assert_fails_with('boom', function(){ assert_equal(1, 2, 'boom'); }); // Fails with "The enclosed code did not fail at all but was expected to fail with boom" because // there is no failing code in there assert_fails_with('boom', function(){ assert_equal(1, 1); }); // Fails with "The enclosed code failed with boom but should have failed with bada" assert_fails_with('bada', function(){ assert_equal(1, 2, 'boom'); }); // Fails with "Something did not explode the way it should!" assert_fails_with('bada', 'Something did not explode the way it should!', function(){ assert_equal(1, 2, 'boom'); });