tushman.io

The musings of an insecure technologist

Python | How I Test

Before starting working with Python a year ago, I was primarily using Ruby, and became a big fan of Behavior Driven Development with the excellent Cucumber. Cucumber and similar tools leverage a testing domain specific language called Gherkin to define behavior in human (read: Product Manager) readable plan text

image

Testing is much more of an art then a science, but in my two plus years in working with in BDD I have come up with some best practices that you may find useful.

After moving to python I looked for a cucumber equivalent port and found the package lettuce which has worked great for me — but I had to do some customization to get to work my testing philosophy.

  1. Each Scenario should not have any effect on any other scenario
  2. Scenario Steps should have access to state from previous Steps
  3. Do not rely on advanced regex for step matching
  4. I am using Flask so, I need to do some magic to get the server running during my tests
  5. Last, I like to see how much code coverage I have, so have also configured coverage.py into our testing

When working with lettuce, most configuration happens in your terrain.py file. In the rest of this post I will show the snip its to implement each of these best practices.

To implement most of these practices we leverage lettuce’s hooks.

  1. Each Scenario should not have any effect on any other scenario

So the gherkin format breaks up each scenario into three parts ‘Given’, ‘When’, and ‘Then’.

Given Build up the datastore to established state When The meat of the test Then Your assertions

In the Given steps I use factories to build up the objects necessary for the test. In ruby land I used factory_girl, and in python land I now use, you guessed it, factory_boy

I am using mongo, and my clean_database function looks like this. But you should adapt this method to your datastore of choice. In ruby land there is a gem called database_cleaner that you can look to for inspiration.

1
2
3
4
@before.each_scenario
def clean_database():
  db = get_db()
  db.connection.drop_database(DATABASE_NAME)

Comments