||This page offers some details about writing integration tests for web/browser pages. || ||<>|| = Introduction = Now that we've upgraded to Zope 3.2 we can use `zope.testbrowser` to do page testing, instead of the traditional way using `http()` calls. `testbrowser` page tests are easier to write and understand, since you get easy access to links and form controls in the page. So, for example, you can check whether a link is present, and click on that link to go to some other page. `zope.testbrowser` can be tricky to use sometimes, though, since you can get weird errors. In the pagetests there is already a 'browser' object set up ready to use. It has `browser.handleErrors = False`, to give you a traceback instead of a simple 500 error message if something goes wrong. It will also give a traceback for 404 pages and pages you don't have permission to view. == How to write testbrowser tests == Read `lib/zope/testbrowser/README.txt` or the [[http://pypi.python.org/pypi/zope.testbrowser#detailed-documentation|online version]], to find out everything you can do with the testbrowser. Read `lib/canonical/launchpad/testing/pages.py` to find out how you can use page testing helpers. Read `lib/canonical/launchpad/pagetests/README.txt` to find out how to run page tests and the available configured users. These are the highlights. * Open a page using `browser.open()`: {{{ browser.open('http://localhost/')}}} There are a few ways to make sure that after an `open`, you are on the right page. It is generally better to show a page title than to print the `browser.url` because the latter makes it more difficult to rename URLs. {{{ browser.title}}} However many of our current page tests, ''do'' print the url. You will also definitely want to do this after a redirect to make sure that you end up on the expected page. {{{ browser.url 'http://localhost/'}}} If you want to test that a page is protected by a permission, you have to check for an `Unauthorized` exception. {{{ browser.open('http://localhost/calendar') Traceback (most recent call last): ... Unauthorized...}}} You can find many pagetest helpers in '''lib/canonical/launchpad/testing/pages.py''', so before starting to write any pagetest check that file out. == Using multiple browsers == If you test multiple HTTP situations -- such as logging in as different people -- in the same file, you need to use different `Browser()` objects. There are three available automatically: * `admin_browser` (a Launchpad administrator) * `user_browser` (someone with no special privileges) * `anon_browser` (visiting Launchpad while logged out). To create another one: {{{ from zope.testbrowser.testing import Browser my_browser = Browser() }}} Or to test as a particular logged-in person: {{{ my_browser = setupBrowser(auth='Basic test@canonical.com:test') }}} == Testing the contents of the page == * To check the contents of the page, print `browser.contents`. Unfortunately the expected output can't begin with '...', so we need to include the start of the page. {{{ print browser.contents ` section to be 'launchpad_ftest'. Next, stick a pdb break point at the place in your page test that you want to examine: {{{ >>> import pdb; pdb.set_trace() }}} Now run the page test as you normally would using `bin/test`. When you hit the break point, go to a different shell and do 'make run'. Because of your dbname change, this will run the site against the testing database, so all the state your page test has built up will be available to you. Now poke around in your browser all you want. Once your page test ends (maybe because you've killed it), your browser session will be unhappy because the launchpad_ftest database is torn down when bin/test exits. Make sure you revert your launchpad.conf changes before you commit. (BarryWarsaw plans on making this easier when lazr config lands.) == Dealing with old-style page tests == There's no reason to rewrite existing page tests, but if some test starts to fail, it's often easier to rewrite it using `zope.testbrowser` than to fix the existing one. For example, if you have a page test that submits a form, and the test is to change a single field in the form, the test might start failing if some required field (with a default value) gets added to the form. The traditional pagetest would fail since the required field is missing. Rewriting it using `zope.testbrowser` would look something like this: {{{ browser.open(...) browser.getControl('Some Field').value = 'foo' browser.getControl('Save').click()}}} That is, it doesn't have to care about the other field values, they will have the default values. == Organizing tests into stories == Prefer standalone tests and short stories rather than long stories. [[DevelopmentMeeting20061221|source]] == Unresolved issues == There's currently no equivalence to `makepagetest.py` in RF for creating `zope.testbrowser` tests, but there is another package, `zope.testrecorder`, which we could pull in. ---- CategoryTesting