.. _id.practicaltips:

=========================
Practical Tips on Testing
=========================

This chapter contains a collection of tips on test strategies and tools, such
as test automation libraries, that help you make BDD a successful experience.

Seriously, Don't Test the User Interface
========================================

.. warning::

    While you can use :pypi:`behave` to drive the "user interface" (UI) or
    front-end, interacting with the model layer or the business logic, e.g.
    by using a REST API, is often the better choice.

    And keep in mind, BDD advises your to test **WHAT** your application
    should do and not **HOW** it is done.

If you want to test/exercise also the "user interface", it may be a good idea
to reuse the feature files, that test the model layer, by just replacing the
test automation layer (meaning mostly the step implementations).
This approach ensures that your feature files are technology-agnostic,
meaning they are independent how you interact with "system under test" (SUT) or
"application under test" (AUT).

For example, if you want to use the feature files in the same directory for
testing the model layer and the UI layer, this can be done by using the
``--stage`` option, like with:

.. code-block:: bash

    $ behave --stage=model features/
    $ behave --stage=ui    features/  # NOTE: Normally used on a subset of features.

See the :ref:`id.appendix.more_info` chapter for additional hints.

Automation Libraries
====================

With *behave* you can test anything on your application stack: front-end
behavior, RESTful APIs, you can even drive your unit tests using Gherkin
language.  Any library that helps you with that you usually integrate by
adding start-up code in ``before_all()`` and tear-down code in ``after_all()``.

The following examples show you how to interact with your Python application
by using the web interface (see `Seriously, Don't Test the User Interface`_
above to learn about entry points for test automation that may be better
suited for your use case).


Selenium (Example)
------------------

To start a web browser for interaction with the front-end using
:pypi:`selenium` your ``environment.py`` may look like this:

.. code-block:: python

    # -- FILE: features/environment.py
    # CONTAINS: Browser fixture setup and teardown
    from behave import fixture, use_fixture
    from selenium.webdriver import Firefox

    @fixture
    def browser_firefox(context):
        # -- BEHAVE-FIXTURE: Similar to @contextlib.contextmanager
        context.browser = Firefox()
        yield context.browser
        # -- CLEANUP-FIXTURE PART:
        context.browser.quit()

    def before_all(context):
        use_fixture(browser_firefox, context)
        # -- NOTE: CLEANUP-FIXTURE is called after after_all() hook.

In your step implementations you can use the ``context.browser`` object to
access Selenium features.  See the `Selenium docs`_ (``remote.webdriver``) for
details. Example using :pypi:`behave-django`:

.. code-block:: python

    # -- FILE: features/steps/browser_steps.py
    from behave import given, when, then

    @when(u'I visit "{url}"')
    def step_impl(context, url):
        context.browser.get(context.get_url(url))

.. _Selenium docs: https://seleniumhq.github.io/selenium/docs/api/py/api.html


Splinter (Example)
------------------

To start a web browser for interaction with the front-end using
:pypi:`splinter` your ``environment.py`` may look like this:

.. code-block:: python

    # -- FILE: features/environment.py
    # CONTAINS: Browser fixture setup and teardown
    from behave import fixture, use_fixture
    from splinter.browser import Browser

    @fixture
    def splinter_browser(context):
        context.browser = Browser()
        yield context.browser
        context.browser.quit()

    def before_all(context):
        use_fixture(splinter_browser, context)


In your step implementations you can use the ``context.browser`` object to
access Splinter features.  See the `Splinter docs`_ for details.  Example
using *behave-django*:

.. code-block:: python

    # -- FILE: features/steps/browser_steps.py
    from behave import given, when, then

    @when(u'I visit "{url}"')
    def step_impl(context, url):
        context.browser.visit(context.get_url(url))

.. _Splinter docs: http://splinter.readthedocs.io/en/latest/


Visual Testing
--------------

Visually checking your front-end on regression is integrated into *behave* in
a straight-forward manner, too.  Basically, what you do is drive your
application using the front-end automation library of your choice (such as
Selenium, Splinter, etc.) to the test location, take a screenshot and compare
it with an earlier, approved screenshot (your "baseline").

A list of visual testing tools and services is available from Dave Haeffner's
`How to Do Visual Testing`_ blog post.

.. _How to Do Visual Testing:
    http://testautomation.applitools.com/post/105435804567/how-to-do-visual-testing-with-selenium
