Source

blog / source / en_posts / testing_python / tdd-bdd.rst

Full commit
=============
 TDD and BDD
=============

.. note::

    This page is a part of :doc:`../2011-03-09-testing-python`
    post.

Now I want to go away from practice to theory again and talk a little
about two things: TDD and BDD. TDD will go first.

-----
 TDD
-----

The most common mistake in the testing world is to mess TDD and
(unit-) testing in general. TDD is not just unit-testing, it's much
more. TDD is a way to formalize process of developing software into
something that more seems like a compiler-driven development, where
instead of compiler trying to figure out errors as you develop small
pieces, you write tests to figure them out.

Robert Martin defines `3 rules of TDD
<http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd>`_ as
this:

- You are not allowed to write any production code unless it is to
  make a failing unit test pass.
- You are not allowed to write any more of a unit test than is
  sufficient to fail; and compilation failures are failures.
- You are not allowed to write any more production code than is
  sufficient to pass the one failing unit test.

As you can see, if your whole team of programmers would work this way,
your codebase would keep being stable all the time, and it would
evolve little by little, no matter how complex the tasks are.

From my observations, when you work with lots of "state" or when you
need to prototype as you develop, TDD is hard to adopt, and it's
sometimes even becomes absurd to see how people transform TDD into
"almost-TDD" or "better-TDD" by weaken some of TDD rules (or adding
exceptions). I actually did that too. And now I see that in places
like "best software practices.ppt" inside my company.

I believe you shouldn't add any exceptions in these rules of TDD or
try to make it "more real-world from your perspective". Programmers
are smart enough to understand if it's not fit for they're tasks or if
they want to add some exceptions into these rules.

-----
 BDD
-----

Different idea of upgrading testing experience is `Behaviour-Driven
Development
<http://en.wikipedia.org/wiki/Behavior-driven_development>`_. The main
idea is that you need to keep constantly focus on what you test and
why you should do that, as a result Dan North created a `JBehave`
framework, which was the same as `JUnit` but with test methods called
with prefix `should_*` instead of `test_*`. BDD has whole filosophy
behind that, you can go and read wikipedia and other things about BDD
(for example, frameworks that let you describe your tests as plain
text).

As lesson from that super-idea, I now call all my tests with prefix
``test_should_`` (as you have probably already saw). That really helps
focusing on what test does at time of test-naming. Of course, BDD is
not just about naming your tests, but the only visible part is prefix
one (at my tests). I am still investigating frameworks that let you
describe test as text, but didn't have experience with that.

-----------------------
 Ping-pong programming
-----------------------

As a bonus, there's a technique called "ping-pong programming" when
one person writes a test, and another implements it. Then they
switch.

-----------------------------------
 Writing test from action to mocks
-----------------------------------

To write tests, you start from it's name (and focus on what it should
do), like:

.. code-block:: python

    def test_should_sort_by_name(self):
        pass

Then you go and write what it should actually do (call the action)
with "``# do``" comment before that.

.. code-block:: python

    def test_should_sort_by_name(self):
        # do
        bl_shopping_list.sort_from_params(query, params)

Then, you go and fill all the necessary mocks and args for your action
to run:

.. code-block:: python

    def test_should_sort_by_name(self):
        query = Mock()
        params = {'sort': '-name'}

        # do
        bl_shopping_list.sort_from_params(query, params)

At the end, you write your assert.

.. code-block:: python

    def test_should_sort_by_name(self):
        query = Mock()
        params = {'sort': '-name'}

        # do
        bl_shopping_list.sort_from_params(query, params)

        query.order_by.assert_called_with('-name', '-updated_at')


And at last, if necessarily, you will add all the ``@patch`` before
method. That's an idea of building a test without a lot of thinking
about what to start from, and moving step-by-step from smaller pieces
to whole-picture of test.

Let's move on to :doc:`change-the-way`.