Welcome, guest | Sign In | My Account | Store | Cart

Notice! PyPM is being replaced with the ActiveState Platform, which enhances PyPM’s build and deploy capabilities. Create your free Platform account to download ActivePython or customize Python with the packages you require and get automatic updates.

Download
ActivePython
INSTALL>
pypm install ftw.testing

How to install ftw.testing

  1. Download and install ActivePython
  2. Open Command Prompt
  3. Type pypm install ftw.testing
 Python 2.7Python 3.2Python 3.3
Windows (32-bit)
1.2
1.4Never BuiltWhy not?
1.2 Available View build log
Windows (64-bit)
1.2
1.4Never BuiltWhy not?
1.2 Available View build log
Mac OS X (10.5+)
1.2
1.4Never BuiltWhy not?
1.2 Available View build log
Linux (32-bit)
1.2
1.4Never BuiltWhy not?
1.2 Available View build log
Linux (64-bit)
1.4 Available View build log
1.2 Available View build log
 
Author
License
GPL2
Lastest release
version 1.4 on Jan 9th, 2014

ftw.testing

This package provides helpers for writing tests.

http://onegov.ch/approved.png/image

Certified: 01/2013

Browser testing with splinter

Splinter is a library which provides a common browser API with a driver for zope.testbrowser.

The ftw.testing package provides integration of Splinter with Plone using Page Objects.

For using the splinter features, use the splinter extras require:

ftw.testing [splinter]
Setting a package up for browser tests

It's easy to setup your package for browser tests:

  • Add a test-dependency to ftw.testing in your setup.py:

System Message: ERROR/3 (<string>, line 37)

Unknown directive type "code".

.. code:: python

    tests_require = [
        'ftw.testing[splinter]',
        ]

    setup(name='my.package',
          ...
          tests_require=tests_require,
          extras_require=dict(tests=tests_require),
          )

  • In your testing.py use the FunctionalSplinterTesting layer wrapper:

System Message: ERROR/3 (<string>, line 51)

Unknown directive type "code".

.. code:: python

    from ftw.testing import FunctionalSplinterTesting
    from plone.app.testing import PLONE_FIXTURE
    from plone.app.testing import PloneSandboxLayer
    from plone.app.testing import applyProfile
    from zope.configuration import xmlconfig


    class MyPackageLayer(PloneSandboxLayer):

        defaultBases = (PLONE_FIXTURE,)

        def setUpZope(self, app, configurationContext):
            import my.package
            xmlconfig.file('configure.zcml', my.package)

        def setUpPloneSite(self, portal):
            applyProfile(portal, 'my.package:default')


    MY_PACKAGE_FIXTURE = MyPackageLayer()
    MY_PACKAGE_FUNCTIONAL_TESTING = FunctionalSplinterTesting(
        bases=(MY_PACKAGE_FIXTURE, ),
        name="my.package:functional")

  • Write tests using the Plone Page Objects:

System Message: ERROR/3 (<string>, line 79)

Unknown directive type "code".

.. code:: python

    from ftw.testing import browser
    from ftw.testing.pages import Plone
    from my.package.testing import MY_PACKAGE_FUNCTIONAL_TESTING
    from plone.app.testing import SITE_OWNER_NAME
    from plone.app.testing import SITE_OWNER_PASSWORD
    from unittest2 import TestCase


    class TestDocument(TestCase):

        layer = MY_PACKAGE_FUNCTIONAL_TESTING

        def test_add_document(self):
            Plone().login(SITE_OWNER_NAME, SITE_OWNER_PASSWORD)
            Plone().visit_portal()
            Plone().create_object('Page', {'Title': 'Foo',
                                           'Body Text': '<b>Hello World</b>'})
            self.assertTrue(browser().is_text_present('Hello World'))


Writing Page Objects

Write your own Page Objects for your views and content types. Put a module pages.py in your tests folder:

System Message: ERROR/3 (<string>, line 107)

Unknown directive type "code".

.. code:: python

    from ftw.testing.pages import Plone


    class MyContentType(Plone):

        def create_my_content(self, title, text):
            self.create_object('MyContent', {'Title': title,
                                             'Body Text': text})
            return self

The Page Object should have methods for all features of your view.

Using the Plone Page Objects

The Plone page object provided by ftw.testing already has the most important features built in, such as:

  • portal_url handling
  • Login
  • Accessing Headings, <body>-CSS-classes, status messages
  • Adding content
  • TinyMCE handling

Currently it's best to just look in the page object code.

MockTestCase

ftw.testing provides an advanced MockTestCase which provides bases on the plone.mocktestcase MockTestCase.

System Message: ERROR/3 (<string>, line 146)

Unknown directive type "code".

.. code:: python

    from ftw.testing import MockTestCase


The following additional methods are available:

self.providing_mock(interfaces, *args, **kwargs)
Creates a mock which provides interfaces.
self.mock_interface(interface, provides=None, *args, **kwargs)
Creates a mock object implementing interface. The mock does not only provide interface, but also use it as specification and asserts that the mocked methods do exist on the interface.
self.stub(*args, **kwargs)
Creates a stub. It acts like a mock but has no assertions.
self.providing_stub(interfaces, *args, **kwargs)
Creates a stub which provides interfaces.
self.stub_interface(interface, provides=None, *args, **kwargs)
Does the same as mock_interface, but disables counting of expected method calls and attribute access. See "Mocking vs. stubbing" below.
self.set_parent(context, parent_context)
Stubs the context so that its acquisition parent is parent_context. Expects at least context to be a mock or a stub. Returns the context.
self.stub_request(interfaces=[], stub_response=True, content_type='text/html', status=200)
Returns a request stub which can be used for rendering templates. With the stub_response option, you can define if the request should stub a response by itself. The other optional arguments: content_type: Defines the expected output content type of the response. status: Defines the expected status code of the response.
self.stub_response(request=None, content_type='text/html', status=200))
Returns a stub response with some headers and options. When a request is given the response is also added to the given request. The other optional arguments: content_type: Defines the expected output content type of the response. status: Defines the expected status code of the response.
self.assertRaises(*args, **kwargs)
Uses unittest2 implementation of assertRaises instead of unittest implementation.

It also fixes a problem in mock_tool, where the getToolByName mock had assertions which is not very useful in some cases.

Mocking vs. stubbing

A mock is used for testing the communication between two objects. It asserts method calls. This is used when a test should not test if a object has a specific state after doing something (e.g. it has it's attribute xy set to something), but if the object does something with another object. If for example an object Foo sends an email when method bar is called, we could mock the sendmail object and assert on the send-email method call.

On the other hand we often have to test the state of an object (attribute values) after doing something. This can be done without mocks by just calling the method and asserting the attribute values. But then we have to set up an integration test and install plone, which takes very long. For testing an object with dependencies to other parts of plone in a unit test, we can use stubs for faking other (separately tested) parts of plone. Stubs work like mocks: you can "expect" a method call and define a result. The difference between stubs and mocks is that stubs do not assert the expectations, so there will be no errors if something expected does not happen. So when using stubs we can assert the state without asserting the communcation between objects.

Component registry layer

The MockTestCase is able to mock components (adapters, utilities). It cleans up the component registry after every test.

But when we use a ZCML layer, loading the ZCML of the package it should use the same component registry for all tests on the same layer. The ComponentRegistryLayer is a layer superclass for sharing the component registry and speeding up tests.

Usage:

System Message: ERROR/3 (<string>, line 234)

Unknown directive type "code".

.. code:: python

    from ftw.testing.layer import ComponentRegistryLayer

    class ZCMLLayer(ComponentRegistryLayer):

        def setUp(self):
            super(ZCMLLayer, self).setUp()

            import my.package
            self.load_zcml_file('configure.zcml', my.package)

    ZCML_LAYER = ZCMLLayer()

Be aware that ComponentRegistryLayer is a base class for creating your own layer (by subclassing ComponentRegistryLayer) and is not usable with defaultBases directly. This allows us to use the functions load_zcml_file and load_zcml_string.

Mailing test helper

The Mailing helper object mocks the mailhost and captures sent emails. The emails can then be easily used for assertions.

Usage:

System Message: ERROR/3 (<string>, line 261)

Unknown directive type "code".

.. code:: python

    from ftw.testing.pages import Mailing
    import transaction

    class MyTest(TestCase):
        layer = MY_FUNCTIONAL_TESTING

     def setUp(self):
         Mailing(self.layer['portal']).set_up()
         transaction.commit()

     def tearDown(self):
         Mailing(self.layer['portal']).tear_down()

     def test_mail_stuff(self):
         portal = self.layer['portal']
         do_send_email()
         mail = Mailing(portal).pop()
         self.assertEquals('Subject: ...', mail)


Compatibility

Runs with Plone 4.1, 4.2 or 4.3.

Changelog

1.4 (2013-08-26)
  • Add custom mailhost class, remembering the sender and recipient of each email separately. [deif]
  • Deprecate @javascript because Selenium with PhantomJS is too unstable. Removes tests and documentation, the @javascript decorator still works for now but needs to be imported from ftw.testing.browser. [jone]
  • Page objects: add a Plone.visit(obj) function. [jone]
1.3.1 (2013-05-24)
  • Move Mailing helper class to its own module mailing. [deif]
1.3 (2013-05-03)
  • Drop official Plone 4.0 support. [jone]
  • Component registry layer: use isolated ZCML layers. When using the same layer instances it may conflict with integration or functional testing layers. [jone]
  • Add splinter integration and Plone page objects. [jone]
  • onegov.ch approved: add badge to readme. [jone]
  • MockTestCase: Support Products.PloneHotfix20121106 patch when mocking getToolByName. [jone]
  • MockTestCase: add checks that setUp is called correctly. [jone]
1.2 (2012-05-22)
  • Add stub_reponse method to MockTestCase and adjust the stub_request method accordant. [phgross]
  • Made providing interfaces configurable for the stub_request method. [phgross]
  • Let the stub_request method also stub the getStatus of the response. [phgross]
  • Add stub_request method to MockTestCase. [jone]
  • No longer tear down the component registry in mock test case. Use the ComponentRegistryLayer. [jone]
  • Add ComponentRegistryLayer base class. [jone]
  • Add mock_interface and stub_interface methods to MockTestCase, creating a mock and using the interface as spec. [jone]
  • Accept also interfaces directly rather than lists of interfaces when creating mocks or stubs which provides the interfaces. [jone]
1.1 (2011-11-16)
  • Patch mock_tool: do not count, so that it can be used multiple times. [jone]
1.0 (2011-10-12)
  • Initial release

Subscribe to package updates

Last updated Jan 9th, 2014

Download Stats

Last month:2

What does the lock icon mean?

Builds marked with a lock icon are only available via PyPM to users with a current ActivePython Business Edition subscription.

Need custom builds or support?

ActivePython Enterprise Edition guarantees priority access to technical support, indemnification, expert consulting and quality-assured language builds.

Plan on re-distributing ActivePython?

Get re-distribution rights and eliminate legal risks with ActivePython OEM Edition.