Unit tests for HTML Output?
This may be a dumb question, but do you make unit tests for the HTML output of your PHP functions/scripts?
I try to keep my HTML and my PHP separate - i.e. HTML includes with placeholders, and functions for certain recurring elements (tabular data / any sort of looped output) - but I'm not sure how to go about verifying this.
Is there a standard way to go about such things, or is it mainly a matter of using regular unit tests on functions which create the inserted con开发者_如何学Ctent, and then making sure it looks correct in the browser/W3C Validator?
Thanks.
Edit: I guess a corollary to this would be: are these sorts of unit tests even worth having? If you're keeping your content and structure properly separated, then you would really only be testing a handful of includes in very limited scenarios (presumably, anyway). Is it really worth it to semi-hand-craft full pages just to have a file to compare to?
Based on my experience in testing HTML, I now follow these three basic rules:
1. Don't test HTML output against a correct template. You will modify the outputted HTML too often, and you'll end up wasting time maintaining your tests.
2. Check for the existence of important data in generated HTML. If you're generating HTML (as opposed to static HTML that you're written once), test the generated HTML for important data. For instance: If you're generating a table based on a two dimensional array, check that the values in the array are found somewhere in the generated HTML. Don't bother to validate the complete output, as this would violate #1.
3. Validate if output is proper HTML. Validate all output for correct HTML in order to avoid stupid mistakes, like missing end tags. I've written a library for this, which can be used absolutely free.
This PHP library will let you validate whether a string is valid HTML5. The library uses Validator.nu. Compatible with PHPUnit or any other testing framework.
Download and documentation here.
Easy to use, example:
$validator=new HTML5Validate();
// Validate (returns TRUE or FALSE)
$result=$validator->Assert('<p>Hello World</p>');
// Get explanation of what's wrong (if validation failed)
print $validator->message;
Testing for HTML output would be considered a coverage test. Initially, when I started using PHP I was creating these tests, but over time I found that these tests weren't really all that helpful.
If there is one thing that I know, it is that the presentation is going to change a lot from initial development to deployment.
If you think about it, a for loop really is not logic but is a isometric transformation function, and if you follow Separation of Concerns
, Then you are passing the data into the for loop via a method of some sort. I would recommend testing that the for loop gets the correct data, but not the output of the for loop.
If you find yourself repeating yourself in generating tables then by all means start unit testing those table templates. But once again, you'll find that those templates will be seeing a lot of change.
At this point you should be looking at separating the iteration from the HTML output to help isolate yourself from these concerns in your tests.
One way to do this is to use a mapping function, it will take a list and transformation function and perform the function on each item in the list, then return the transformed list.
Usually, when creating tables, I end up with two for loops in creating a row.
- Iterate over all rows.
- While in (1) iterate over items in row.
Pretty ugly to unit test that, but with closures you can create function generators that would really be easy [this is said with a grain of salt] to implement.
You can use PHPUnit. It has Output testing.
http://www.phpunit.de/manual/3.0/en/testcase-extensions.html
I found the SimpleTest framework to be very useful, usually i use it for integration-tests and PhpUnit for unit-tests. They spare me a lot of manually submitted formulars, which i would do otherwise over and over again.
It became my habit to follow this points, when doing such integrations tests:
- Try not to repeat tests that are already done with real unit-tests. If for example you have a unit-tested validating function for email addresses, it doesn't make sense to submit all kind of invalid email addresses. Only check once if you are redirected with an error message.
- Do not compare the resulting HTML with a complete reference output, you would have to update your tests with every redesign of your pages. Instead check only crucial parts with
$webTestCase->assertText('...');
or$webTestCase->assertPattern('/.../');
.
With some tiny helper functions, you can gain a lot of robustness. The following function will open a page and checks if the page was opened successfully without warnings. Since there is no compiler for PHP that can give out warnings at design time, you can at least make sure that your code will not produce errors or warnings.
public static function openPageWithNoWarnings($webTestCase, $page, $landingPage = null)
{
// check that page can be opened successfully
$webTestCase->assertTrue($webTestCase->get($page));
// check that there are no PHP warnings
$webTestCase->assertNoPattern('/(warning:|error:)/i', 'PHP error or warning on page!');
// check if landed on expected page (maybe a redirect)
if (!empty($landingPage))
{
$url = $webTestCase->getUrl();
$file = basename(parse_url($url, PHP_URL_PATH));
$webTestCase->assertEqual($page, $file,
sprintf('Expected page "%s", got page "%s".', page, $file));
}
}
Such tests will give you not much of work, you can start with very light tests, but they give you instantly feedback if something fails, with only one mouse click.
There is an extension for PHPUnit that does html validation here: https://github.com/xvoland/html-validate
Running into this question myself. I think an approach might be to use something like phpQuery to make your tests less fragile. Instead of testing for exact output, test that there should be an h3 tag ~somewhere~ in the output. If it gets wrapped in a div later because a designer needed to tack on an extra background, or because of some ie6 float bug workaround, then your test still works.
It's not very pure, but still potentially a very useful tool.
In some cases (such as CakePHP Helpers), the purpose of a class or function is to generate consistent HTML for you. In such cases, it's important to test that the expected properties of the generated unit of HTML are correct for given inputs. The question is definitely valid in that context.
PHPUnit provides an assertTag() function for this purpose.
However to echo the others; it's important to stress that unit testing should be done on the smallest possible components of your project, and not entire rendered web pages. There are other tools (Selenium for example) that are designed for ensuring that those individual components are integrated together properly.
One very simple way to do this is with output buffering.
eg
ob_start();
function_which_produces_some_output();
$this->assertEquals( ob_get_clean(), '<p>Expected Output Here</p>');
A lot of these links are either dead or old. Take a look at http://phpfui.com/PHPFUI/HTMLUnitTester It validated HTML and CSS against w3.org standards. I wrote it because I could not find any recent and currently supported PHP library to do it. Other solutions were based on an old version of PHPUnit or not compatible with modern PHP code.
It uses the W3C HTML validator service which I would recommend running locally. It is all explained in the readme file in configuration. The beauty of the library is that it does not do the actual validation, but calls the W3C.org code, so it will always be up to date if you keep your local install current.
Hope this helps.
精彩评论