关于lettuce的一个基本操作的总结
第一次完完整整的看完一个英文版文档,虽然字数不多,但对于我这样的菜鸟而言还是值得鼓励的。
Python BDD-lettuce
[总结于http://lettuce.it/]
Directory Structure:
|tests
|features
-zero.feature
-steps.py
-terrain.py
Scenarios:
There are two kinds of Scenarios:
1.Simple:
Feature: Apply all my friends to attend a conference
In order to apply all my friends to the next PyCon_
As a lazy person
I want to fill the same form many times
Scenario: Apply Mary
Go to the conference website
Access the link "I will attend"
Fill the field "name" with "Mary"
Fill the field "email" with "mary@domain.com"
Fill the field "birthday" with "1988/02/10"
Click on "confirm attendance" button
2.Outlined:
Feature: Apply all my friends to attend a conference
In order to apply all my friends to the next PyCon_
As a lazy person
I want to fill the same form many times
Scenario Outline: Apply my friends
Go to the conference website
Access the link "I will attend"
Fill the field "name" with "<friend_name>"
Fill the field "email" with "<friend_email>"
Fill the field "birthday" with "<friend_birthdate>"
Click on "confirm attendance" button
Examples:
| friend_name | friend_email | friend_birthdate |
| Mary | mary@domain.com | 1988/02/10 |
| Lincoln | lincoln@provider.net | 1987/09/10 |
| Marcus | marcus@other.org | 1990/10/05 |
Steps:
Comparable with Scenarios,Steps comes in two kinds:
1.Simple:
Given I go to the conference website
2.Tabular:
Given I have the following contacts in my database
| name | phone |
| John | 2233-4455 |
| Smith | 9988-7766 |
Handling data with tables:
Example:
Feature:
Scenario: Bill students which name starts with "G"
Given I have the following students in my database:
| name | monthly_due | billed |
| Anton | $ 500 | no |
| Jack | $ 400 | no |
| Gabriel | $ 300 | no |
| Gloria | $ 442.65 | no |
| Ken | $ 907.86 | no |
| Leonard | $ 742.84 | no |
Steps:
@step(‘I have the following students in my database:’)
Def test(step):
For t in step.hashes:
Person = Student(**student_dict)
Person.save()
Getting the first or the last row of the tables:
@step(‘I have the following students in my database:’)
Def test(step):
Person1 = Student(**step.hashes.first)
Person2 = Student(**step.hashes.last)
Person1.save()
Person2.save()
Note:Every step has a attribute called hashes which is a list of dict.Each dict has represents table headers as keys and each table row as value.(table header : key,table row : value)
As follows:
@step(‘I have the following students in my database:’)
Def test(step):assert step.hashes == [
{
'name': 'Anton',
'monthly_due': '$ 500',
'billed': 'no'
},
{
'name': 'Jack',
'monthly_due': '$ 400',
'billed': 'no'
},
{
'name': 'Gabriel',
'monthly_due': '$ 300',
'billed': 'no'
},
{
'name': 'Gloria',
'monthly_due': '$ 442.65',
'billed': 'no'
},
{
'name': 'Ken',
'monthly_due': '$ 907.86',
'billed': 'no'
},
{
'name': 'Leonard',
'monthly_due': '$ 742.84',
'billed': 'no'
},
]
Multi-line strings:
Can see : http://lettuce.it/tutorial/multiline.html
Calling steps from step definitions:
When we want to re-use steps that we’ve seen before,how we to do?
“step of steps”
@step('I am logged in')
def is_logged_in(step):
step.given('I go to the home page')
step.given('I click the login button')
“blocks of steps”
@step('I am logged in')
def is_logged_in(step):
step.behave_as("""
Given I go to the home page
And I click the login button
And I fill in username:{user} password:{pass}
And I click "Login"
""".format(user='floppy', pass='banana'))
The command line
Note: Lettue is used as a command line utility,it means that currently the only way to use it is through a shell.
Running a specific feature file:
Lettuce path/to/some/file.feature
Running only some scenarios of a specific feature file:
Lettuce path/to/some/file.feature -s 3,5,9
:this will run the scenarios 3,5 and 9 from the file
Verbosity level:
Lettuce --verbosity=1|2|3
Lettue -v 1|2|3
integrating with continuous integration(结合持续集成):
Lettuce can use Subunit to output test results.Subunit is a stream format that can be multiplexed(多路输出),viewed in real time or converted to many different formats(such as xUnit/jUnit XML format)
Lettuce --with-subunit > output.log
Subunit2junitxml<subunit.bin>lettucetests.xml
Coverage run lettuce --with-subunit
Coverage xml
Features,scenarios and steps reference
Can see : http://lettuce.it/reference/features.html
The “terrain”
Its about setup and teardown
By convention lettuce tries do load a file called terrain.py located at the current directory.
Think at this file as a global setup place,there you can setup global hooks,and put things into lettuce “world”
World:
To set the variable global
World.absorb:
To set the function/class global
As follows:
from lettuce import world
def my_project_wide_function():
# do something
world.my_project_wide_function = my_project_wide_function
world.my_project_wide_function()
----->
from lettuce import world
@world.absorb
def my_project_wide_function():
# do something
world.my_project_wide_function()
And even with lambdas,but in this case you need to name it:
from lettuce import world
world.absorb(lambda: "yeah", "optimist_function")
assert world.optimist_function() == 'yeah'
World.spew
Q:
If keep stashing things in lettuce.world,it may bloat it sometime, or confuse member names along my steps,or hooks.
A:
We can spew it.
from lettuce import world
@world.absorb
def generic_function():
# do something
assert hasattr(world, 'generic_function')
world.spew('generic_function')
Hooks:
Lettuce has hooks that are called sequentially(有序调用) before and after each action
Presented as python decorators(装饰器)
@before.all
Note: this decorated function takes no parameters
from lettuce import *
@before.all
def say_hello():
print "Hello there!"
print "Lettuce will start to run tests right now..."
@after.all
Note: the decorated function takes a TotalResult as parameter
from lettuce import *
@after.all
def say_goodbye(total):
print "Congratulations, %d of %d scenarios passed!" % (
total.scenarios_ran,
total.scenarios_passed
)
print "Goodbye!"
@before.each_feature
Note: the decorated function takes a Feature as parameter
from lettuce import *
@before.each_feature
def setup_some_feature(feature):
print "Running the feature %r, at file %s" % (
feature.name,
feature.described_at.file
)
@after.each_feature
The same as @before_feature
@before.each_scenario,@after.each_scenario
from lettuce import *
from fixtures import populate_test_database
@before.each_scenario | @after.each_scenario
def setup_some_scenario(scenario):
populate_test_database()
@before.each_outline | @after.each_outline
Note: the parameter : ”outline” is the dictionary
from lettuce import *
@before.each_outline
def setup_some_scenario(scenario, outline):
print "We are now going to run scenario {0} with size={1}".format(scenario.name, outline["size"])
@before.each_step
from lettuce import *
@before.each_step | @after.each_step
def setup_some_step(step):
print "running step %r, defined at %s" % (
step.sentence,
step.defined_at.file
)
Warning: all the handle_request hooks are run within a python thread. If something went wrong within a callback, lettuce can get stuck(被卡住).
我遇到的一些问题:
.feature文件中 Feature后必须紧跟一个空格,否则运行时会读取不到feature
在并不是很了解字典相关的时候,我自定义了一个方法作为字典的解析,如果仅仅是写一串{‘key1’:’value1’,‘key2’:’value2’,...},lettuce获取到并传递的时候,requests会报错400(传输数据的格式有误),虽然我尝试过,json在python中的格式其实也就是一个str,这点不是很理解。
def str_to_dict(str):
temp_dict = {}
temp_result = str.split(",")
for key in temp_result:
temp_key = key.split(":")
temp_dict[temp_key[0].strip()]=temp_key[1].strip()
return temp_dict
运行的时候基本遇到中文就是乱码,这方面资料网上少之甚少,最合理的一个解释就是别去使用dos运行,据说是可以使用eclipse去运行。没尝试过,但这种编码问题仅仅是影响到了我的输出结果,并不影响我的运行,所以也没有过多去关注。