Currently, simpletest browser based tests get data for assertions by parsing the HTML response after a request. Some data may be difficult to extract from the HTML output (e.g. error and status messages) and makes tests dependent of the theme used. Other data is not directly available (e.g. module specific data, the theme used, ...), even when this data can be extracted in a way, the method may be tricky and not accurate at all.
Some examples:
- drupalLogout based it's assertion on finding a field name and a field password. With the extensibility of Drupal this is not much accurate, if we are testing a module that alter the user login form by removing the password field, this will fail. An exact check would be comparing the user uid of the current user with 0.
A similar case is drupalLogin
- While writing a test to check if the user administration theme is being used when a request is done to admin/*, we can check if exist a resource with the same directory path of the theme path, but again, such resource may not exist... Won't be good to have the data about the theme used to serve a request available for assertions ?
- $this->assertNoText($node1->title, 'Alias was successfully deleted.');. Suppose an hypothetical module that provide some help text to this page include a text like: "...If an alias was successfully deleted, then the default path will be used" (it's completely hypothetical). In this case, the test will fail.
In general, basing assertions on text that may be modified before the output, or that may appear in other way, IMHO is not a good idea and is completely dependent of the theme and media used.
Proposition
A possible approach could be to force Drupal to output a XML response when the simpletest user-agent is detected and make the testing system consume XML, instead of the HTML output. In the XML response may be included any data that was used in the request thread and make the data available for assertions. This is an extensible solution, modules can include their data to be used by their test or other tests. The testing system will be independent of the theme and the them layer is still testable since the content returned is included in the XML response as well.
The XML response could look like this:
<simpletest>
<title>User account</title>
<locale lang="" dir="" negotiation="">
<base-path>/drupal7-test/</base-path>
<current-path>user</current-path>
<path-alias>user</path-alias>
<user>0</user>
<messages>
<message type="error">notice: Undefined variable: b in D:\wwwroot\drupal7-test\includes\common.inc on line 3594.</message>
</messages>
<modules-data>
<example>
<example-module-custom-data></example-module-custom-data>
</example>
</modules-data>
<content>
<![CDATA[
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
...
</html>
]]>
</content>
</simpletest>
While other format can be used to implement this idea, IMO, XML is the more suitable due to its features like extensibility, readability, portability, security; and can be parsed in a similar way the system is parsing the XHTML output now.
Patch
The patch adds an initial implementation for this. Introduces a new function drupal_response which is called from index.php. That function detects the user-agent and serve the XML output if the simpletest user agent is detected, using the simpletest.tpl.php template.
Some minor modifications to the simpletest module classes is required to consume the XML instead of the HTML output.
| Comment | File | Size | Author |
|---|---|---|---|
| #14 | xml-output-simpletest-14.patch | 10.97 KB | dropcube |
| #13 | xml-output-simpletest-13.patch | 11.47 KB | dropcube |
| #10 | xml-output-simpletest.patch | 11.59 KB | boombatower |
| #9 | xml-output-simpletest-7.patch | 11.18 KB | dropcube |
| #4 | xml-output-simpletest.patch | 4.87 KB | dropcube |
Comments
Comment #1
damien tournoud commentedGlobally, I'm against that sort of approach.
What we are testing with functional tests is the operation of a complete product (Drupal), with its default theme and with some specific modules activated. We don't test the whole variability of parameters (what is we use a different theme? what if we enable all modules from the contrib repository? what if I play the guitar while Drupal is testing?).
I think this adds too much complexity, adds maintenance burden and simply change the way the product behave (you know, monitoring just changes the way the system behaves).
However, there is real value in fetching some specific structured information from the tested site, especially about error messages and PHP exceptions. I suggested in http://drupal.org/node/243532 to use HTTP headers to convey that information, but there are probably other ways to do this.
Damien
Comment #2
dropcube commented@Damien:
Maybe in this phase we are only testing core modules, but Simpletest is not intended only for core, is it ? In my opinion it should be as powerful and extensible as Drupal is and can be used to test any related Drupal project.
Relying on the default theme's markup for assertions makes the testing system dependable of that theme. If we are testing a complete product, core tests must pass, no matter what theme or modules you are enabling.
I do not agree with this. Where is the complexity ? In my opinion it reduces the complexity instead of adds to much complexity. The behavior of the system is not changed in any way. If a special user-agent is detected, it just respondes with a different template. The approach of detecting the user-agent is being used in core already. There is more complexity in the way that assertions are implemented now, often using tricky XPath expressions to get the data from the HTML output. Note, for example, a change in the class of an HTML element in the default theme, may breaks some assertions methods.
Anyway, if we need the real HTML output, we will have it in
<content></content>.The approach of HTTP headers could work, but is not as extensible as XML is. Instead of injecting HTTP headers, isn't better to do this in the Drupal way?, just theming whatever we need to output.
Comment #3
boombatower commentedIt is not intended for core only, you are correct it should work for any module. Now obviously if a contrib module changes core functionality core tests may not pass, but since SimpleTest uses fresh installs and only installs the requested modules (requested by test) that should be a problem.
Testing the theme should be done somewhat independently, at least that is my thought. Having a SimpleTest theme would ensure that you are testing the modules behavior and not the theme. Separate tests should be written to test theme layer with various templates, etc. The main concern for testing is to eliminate variables outside the scope of the test, the theme layer is one of those.
I don't see much point in sending special HTTP headers since the data can just as easily be parsed using xpath or XML which are more complete solutions.
Comment #4
dropcube commentedHow to test the patch
- Install Drupal using the string simpltest1 as the database prefix, or any string that matches
/^simpletest\d+$/.- Apply the patch.
- Now you guys can use for this Firefox with the user Agent Switcher Addon. Create a new User Agent, and use the same values as the database prefix. Then switch to this User Agent to simulate a simpletest request. When a request is made using the simpletest user agent, the output will be an XML as described above. Other browsers like Safari can be used with custom user agents too.
Comment #5
catchIf a contrib module is doing stuff that might break core functionality, then as boombatower says, it's not going to break core tests (because at most they install optional core modules). So the tests for the module itself are going have to test those scenarios themselves - which simpletest allows you to do. As such, I'm not sure what problem this is supposed to fix. If string changes in core break core tests then that's a feature of testing, not a bug - same as other changes in core ought to be caught by tests as well.
Comment #6
dropcube commented@catch: Suppose you are testing a custom module that in any way alters the user page (example.com/user) and the user form.
DrupalWebTestCase::drupalLoginanddrupalLogoutbase their assertions on finding the name and the password fields in a login form:So, when testing this module, if you try to logout a user with the default
$this->drupalLogout()method will fail. It's true that you can overwrite this method in your test class, but this is a simple edge case, the real issue is that several assertions are based on the HTML output and relying on the default theme markup in some cases, what makes the testing system dependent of the theme, which isn't .However, if you have the 'real' data that was used in the testing thread (the request to the testing site), tests will be more accurate and won't be basing their assertions on the HTML output, but the data received in the response, in a XML format, for example.
This way, the Logout method may be written this way:
// Load the user page, the idea being if you were properly logged out you should be seeing a login screen.// Load the user page, the idea being if you were properly logged out, the user uid should 0.
In this case, the assertion is based on the data received, and not on the HTML output.
$this->xmlis a SimpleXMLElement object representing the XML data received.Having the data will be useful for a lot of situations, where it can not be found by parsing the HTML output, or is hard to parse it, or is based on markup assumptions. Other good example is parsing the Drupal messages, errors, warnings, and status messages, etc... they can be obtained easily from the XML.
if you want to test the theme layer, well, no problem, you have the real HTML output in the
<content></content>tag of the XML.Comment #7
dropcube commentedHere is a working patch implementing this idea. Note that the testing system is completely functional and the assertions continues working. Only modifications to the parsing method are required, so that the content now is in the
<content></content>tag of the XML response. IF we use this technique, assertions can be improved later, using the data available.Comment #8
dropcube commentedthe patch...
Comment #9
dropcube commentedattachments working ?
Comment #10
boombatower commentedFix a bunch of trailing spaces.
Ran block test and glanced at code...looks good. Don't have enough time to fully review right now, but before this gets into core all the tests will need to be run against it.
Note: Do a find and replace with
regex and you should get all trailing "spaces." If you have your tab policy in your editor set to spaces that should catch everything.
Comment #11
moshe weitzman commenteddrupal_response90 is not so elegant. imo, this is a dupe of #145551: Enable loading and rendering into multiple formats (html, xml, json, atom, etc.)
Comment #12
dropcube commented@moshe: #145551: Enable loading and rendering into multiple formats (html, xml, json, atom, etc.) is a more general case, if that is the direction to go, then the rendering of the XML can be implemented that way. This issue is more related to the idea of making simpletest consume XML than to the rendering process. For now, this method can work, it requires fewer modifications to core and allow us to improve the testing system as described here. IMO, it's critical, the testing process could be easier if go this way.
Comment #13
dropcube commentedPatch rerolled against HEAD.
Comment #14
dropcube commentedPatch rerolled against HEAD.
Comment #15
dropcube commentedNote that the testing system is completely functional after applying the patch. All the assertions continue working because only the parsing method is modified to get the content from the XML tag and not from the raw response.
Comment #16
cwgordon7 commentedSo override the functions. It's OOP.
I am very against this, as it is a quite ugly approach to an almost nonexistent problem. We should be testing Drupal as it is, functionally - not some weird half-Drupal state of random xml data. I am tempted to won't fix this.
Comment #17
dropcube commented@cwgordon7:
Yes, this can be an approach to deal with that issue, but this is not the main issue I am describing here.
We will continue testing functionality, and will continue testing Drupal as it is. We only would be changing the way data is is received. Instead of parsing raw HTML data and basing assertions on data that is subject to change (for example, a simple change to garland theme may break a lot of assertions that are based on finding some markup on the HTML response). Anyway, if you still want to have access to the raw or 'real' HTML data, get if from the XML. I can see several advantages of doing so, we will be relying on correct and well structured data, and not in raw data that is difficult to parse and is state-less.
Why random ?
Comment #18
cwgordon7 commentedRandom in the sense that it has no meaning outside of the automated testing world.
Comment #19
dropcube commentedWhile other format can be used to implement this idea, I think, XML is the more suitable due to its features like extensibility, readability, portability, security; and can be parsed in a similar way the system is parsing the XHTML output now.
Anyway, the idea is more general than using XML, it's more about exposing metadata to be consumed by tests and can be implemented using this approach later #145551: Enable loading and rendering into multiple formats (html, xml, json, atom, etc.)
Comment #20
dropcube commentedOther possible approach may be the use of a simpletest specific theme, where theme functions may be implemented to expose some other data in the output. IMO, we should test Drupal with it's default output, and not with a specific theme, and moreover test each theme and the theme layer. So, a simpletest theme would be Drupal default output + other data exposed to tests (data that can not be parsed from the output or that is not accurate when parsing from the output).
So, if we want to test garland, then we run the garland test and see how are performing garland specific functionality. See #282540: Tests for themes
Also see this comment in this issue #279515: setting an installation profile for a test doesn't run hook_install_tasks()
Comment #21
damien tournoud commentedI still strongly disagree with this approach, for the reasons I outlined in #1.
We are performing "outside testing" of Drupal. We should alter *as less as possible* the object we are trying to test. For now, the only alteration of Drupal core is the special case for database prefix. I suggest to add PHP notice/warning/error reporting to that, because they are important for debugging test results.
But enough is enough: this is a 6 line patch to Drupal core. Yours is more like a hundred.
Again: we are testing Drupal core, not Drupal core with some fancy alterations.
So: some data is hard to get and most assertions are theme specific... Of course: we are testing the full product in its standard configuration! Deal with it.
I'm *very* tempted to won't fix that.
Comment #22
cwgordon7 commentedGiven #268063: Move includes/tests to simpletest/tests & provide hidden .info propery, perhaps it would be possible to make assertions based on hook_preprocess_page() instead?
Comment #23
dropcube commented@Damien:
I agree with you that we strongly need #243532: Catch notices, warnings, errors and fatal errors from the tested side as soon as possible, but this is the time to discuss and find complete and scalable solutions, and not particular solutions to a general problem: the absence of accurate info about the testing thread.
I do not agree. Also see #3
IMHO, we are implementing a testing framework for Drupal, which can be seen as a content management framework or as a complete product (installed with the default profile or with any profile that includes multiple modules or themes), ...
Also you further may want to implement a test for a set of modules to test how they work together, or test an individual theme, ... which testing framework are you going to use? So, it's not only for core...
For example, think in this scenario: I maintain a drupal profile for an intranet, which includes a custom module to deal with LDAP. That module replaces the default Drupal login form with a specific form (requesting other information from the user, not just the username and password). I do not consider this a *fancy* alteration, but a real use case.
It must be able to use the testing framework to test such profiles, which enable certain modules and test how they work/interact with core, when multiple modules are enabled, and seen as a whole.
So, this issue is not only about the use of XML or a simpletest theme, it's about finding a way to expose some data to tests.
OK, if XML or a simpletest theme are not good aproaches, let's think in any other ;)
Comment #24
dropcube commentedopen for discussion.
Comment #25
catch@dropcube, while the framework should be used for testing contrib modules (and integration between contrib modules) - that doesn't mean that core tests need to apply to all configurations - in fact they only enable the minimum possible functionality in setUp() and clean it up when they're done - so an altered login form won't be taken into account during the core tests at all.
If you're testing an altered login form, then you'd want to test that form - so why not just write a test for it? drupalPost() etc. will still work. Or if you're going to be writing lots of other tests for a site which will depend on logging in via that form, then like cwgordon suggested, overwrite the drupalLogin function or implement your own version.
Also I don't think PHP warnings/notices etc. are special cases - they're pretty central to identifying bugs, and they're already displayed by default in development versions of Drupal - the only change is making the test framework pick them up.
Comment #26
dropcube commentedWon't be taken into account if you are running the default profile. Profile developers may want to run all tests using their custom built profile. As per #279515: setting an installation profile for a test doesn't run hook_install_tasks(), would be good to test alternate profiles to make sure it runs correctly.
Currently we have in
setUp()So, profile-installed modules + test case modules...
Absolutely agreed. My point here is that we need to find a way to share such data that is available in the tested thread.
Comment #27
dave reidNo momentum in a year and there is not a strong agreement to accept this into core. Marking as won't fix.