Behave

Behave is a tool for behaviour driven development for Python. So far I have been using hand rolled DSL for some of the behaviour tests, but decided to give behave a go today.

I had earlier written a test with Cutesy and decided to write same tests with behave in order to test the differences:

    def test_dropping_item(self):
        """
        Items dropped by character should end on the floor
        """
        dagger = Dagger()
        Uglak = Goblin(carrying(dagger))
        place(Uglak, middle_of(Level()))

        make(Uglak, drop(dagger))

        assert_that(Uglak, has_dropped(dagger))

Following is my feature specification:

Feature: Dropping items
  as an character
  in order to manage my inventory
  I want to drop items

  Scenario: drop item
     Given Pete is Adventurer
       And Pete is standing in room
       And Pete has dagger
      When Pete drops dagger
      Then dagger should be in room
       And dagger should be at same place as Pete
       And dagger should not be in inventory of Pete
       And time should pass for Pete

Nothign fancy, just a description of the feature and single scenario. It is quite a bit more verbose than the Cutesy-version, but I would think it is also quite a bit more readable for non-programmers. Of course behind the scenes there is some code to implement various steps:

@given(u'{character_name} is Adventurer')
def impl(context, character_name):
    context.characters = []
    new_character = Adventurer()
    new_character.name = character_name
    context.characters.append(new_character)

Not that many lines to implement single step (ok, it’s quite quick and dirty implementation, but still). Interesting point here is that I could reuse function Adventurer, which was originally defined for Cutesy.

Same trend continues as I define more and more steps. Cutesy makes writing tests with behave really nice and easy. Fowler mentioned something along lines that after defining an internal DSL, you can use the same implementation as a basis of external DSL. Basically you just need to add an parser. And that’s exactly what I’m doing here.

Behave has possibility to define parameters for tests. This allows reusing same steps in different tests. In the example here, I have two parameters, character name and item name. They match to character and item created in earlier tests.

@when(u'{character_name} drops {item_name}')
def impl(context, character_name, item_name):

    characters = [x for x in context.characters
                  if x.name == character_name]
    character = characters[0]
    
    items = [x for x in context.items
             if x.name == item_name]
    item = items[0]

    make(character, drop(item))
    assert True

When I run behave, I get following output:

Feature: Dropping items # features\dropping.feature:1
  as an character
  in order to manage my inventory
  I want to drop items

  Scenario: drop item                             # features\dropping.feature:6
    Given Pete is Adventurer                      # features\steps\items.py:8
    And Pete is standing in room                  # features\steps\items.py:16
    And Pete has dagger                           # features\steps\items.py:29
    When Pete drops dagger                        # features\steps\items.py:42
    Then dagger should be in room                 # features\steps\items.py:56
    And dagger should be at same place as Pete    # features\steps\items.py:67
    And dagger should not be in inventory of Pete # features\steps\items.py:79
    And time should pass for Pete                 # features\steps\items.py:91


1 feature passed, 0 failed, 0 skipped
1 scenario passed, 0 failed, 0 skipped
8 steps passed, 0 failed, 0 skipped, 0 undefined
Took 0m0.0s

Behave is pretty strong tool and really easy to use. It took me around half an hour to install it, read tutorial and write first pyherc test (which was ugly, but it worked). I don’t know if I want to use behave more in testing pyherc, or if I continue with DSL-route. Tests written with behave are easier to read for others, but pyherc is currently one man project.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s