本视频由 保利威 提供技术支持

第六讲:日志模块

培训目标

  1. 掌握日志的分级思想,不同级别的日志落点文件,日志内容本身的组织规范。
  2. 掌握常用的断言函数,断言内容本身的构成要素,写法规范。

课程笔记

一、logging

1. 在哪里学习?

文档:https://docs.python.org/3.7/library/logging.config.html
https://docs.python.org/3.7/library/logging.html

我个人推荐的文章:python之配置日志的几种方式|Python之日志处理(logging模块)

2. logging 模块四大组件

组件说明
loggers提供应用程序代码直接使用的接口
handlers用于将日志记录发送到指定的目的位置
filters提供更细粒度的日志过滤功能,用于决定哪些日志记录将会被输出(其它的日志记录将会被忽略)
formatters用于控制日志信息的最终输出格式

二、如何使用 behave

1. 在哪里学习?

2. behave 测试框架由哪些元素构成

https://behave.readthedocs.io/en/latest/tutorial.html#

# -- SIMPLIFIED GHERKIN GRAMMAR (for Gherkin v6):
# CARDINALITY DECORATOR: '*' means 0..N (many), '?' means 0..1 (optional)
# EXAMPLE: Feature
#   A Feature can have many Tags (as TaggableStatement: zero or more tags before its keyword).
#   A Feature can have an optional Background.
#   A Feature can have many Scenario(s), meaning zero or more Scenarios.
#   A Feature can have many ScenarioOutline(s).
#   A Feature can have many Rule(s).
Feature(TaggableStatement):
    Background?
    Scenario*
    ScenarioOutline*
    Rule*

Background:
    Step*           # Background steps are injected into any Scenario of its scope.

Scenario(TaggableStatement):
    Step*

ScenarioOutline(ScenarioTemplateWithPlaceholders):
    Scenario*       # Rendered Template by using ScenarioOutline.Examples.rows placeholder values.

Rule(TaggableStatement):
    Background?     # Behave-specific extension (after removal from final Gherkin v6).
    Scenario*
    ScenarioOutline*

b. step 实现

feature文件:

Scenario: Search for an account
   Given I search for a valid account
    Then I will see the account details

step 代码实现:

@given('I search for a valid account')
def step_impl(context):
    context.browser.get('http://localhost:8000/index')
    form = get_element(context.browser, tag='form')
    get_element(form, name="msisdn").send_keys('61415551234')
    form.submit()

@then('I will see the account details')
def step_impl(context):
    elements = find_elements(context.browser, id='no-account')
    eq_(elements, [], 'account not found')
    h = get_element(context.browser, id='account-head')
    ok_(h.text.startswith("Account 61415551234"),
        'Heading %r has wrong text' % h.text)

c. 环境控制

在environment.py模块中定义了在指定事件发生前后执行的代码

before\_step(context, step), after\_step(context, step)

These run before and after every step.

before\_scenario(context, scenario), after\_scenario(context, scenario)

These run before and after each scenario is run.

before\_feature(context, feature), after\_feature(context, feature)

These run before and after each feature file is exercised.

before\_tag(context, tag), after\_tag(context, tag)

These run before and after a section tagged with the given name. They are invoked for each tag encountered in the order they’re found in the feature file. Seecontrolling things with tags.

before\_all(context), after\_all(context)

These run before and after the whole shooting match.

3. 以上元素的调用顺序

https://behave.readthedocs.io/en/latest/api.html#runner-operation

https://behave.readthedocs.io/en/latest/gherkin.html#background

# -- PSEUDO-CODE:
# HOOK: before_tag(), after_tag() is called for Feature, Rule, Scenario
ctx = createContext()
call-optional-hook before_all(ctx)
for feature in all_features:
    for tag in feature.tags:
        call-optional-hook before_tag(ctx, tag)
    call-optional-hook before_feature(ctx, feature)
    for run_item in feature.run_items:  # CAN BE: Rule, Scenario, ScenarioOutline
        execute_run_item(run_item, ctx)
    call-optional-hook after_feature(ctx, feature)
    for tag in feature.tags:
        call-optional-hook after_tag(ctx, tag)
call-optional-hook after_all(ctx)
 
function execute_run_item(run_item, ctx):
    if run_item isa ScenarioOutline:
        # -- CASE: ScenarioOutline
        # HINT: All Scenarios are already created from Example(s) rows.
        scenario_outline = run_item
        for scenario in scenario_outline.scenarios:
            execute_run_item(scenario, ctx)
    else if run_item isa Scenario:
        # -- CASE: Scenario
        # HINT: Background steps are injected before scenario steps.
        scenario = run_item
        for tag in scenario.tags:
            call-optional-hook before_tag(ctx, tag)
        call-optional-hook before_scenario(ctx, scenario)
        for step in scenario.steps:
            call-optional-hook before_step(ctx, step)
            step.run(ctx)
            call-optional-hook after_step(ctx, step)
        call-optional-hook after_scenario(ctx, scenario)
        for tag in scenario.tags:
            call-optional-hook after_tag(ctx, tag)

4. context 属性的生命周期

https://behave.readthedocs.io/en/latest/context\_attributes.html#user-attributes

三、断言 hamcrest

1. 在那里学习?

文档:https://pyhamcrest.readthedocs.io/en/v2.0.2/

代码:https://github.com/hamcrest/PyHamcrest

2. 预定义的 matcher

*   Object
    *   `equal_to`\- match equal object
    *   `has_length`\- match`len()`
    *   `has_property`\- match value of property with given name
    *   `has_properties`\- match an object that has all of the given properties.
    *   `has_string`\- match`str()`
    *   `instance_of`\- match object type
    *   `none`,`not_none`\- match`None`, or not`None`
    *   `same_instance`\- match same object
    *   `calling, raises`\- wrap a method call and assert that it raises an exception
*   Number
    *   `close_to`\- match number close to a given value
    *   `greater_than`,`greater_than_or_equal_to`,`less_than`,`less_than_or_equal_to`\- match numeric ordering
*   Text
    *   `contains_string`\- match part of a string
    *   `ends_with`\- match the end of a string
    *   `equal_to_ignoring_case`\- match the complete string but ignore case
    *   `equal_to_ignoring_whitespace`\- match the complete string but ignore extra whitespace
    *   `matches_regexp`\- match a regular expression in a string
    *   `starts_with`\- match the beginning of a string
    *   `string_contains_in_order`\- match parts of a string, in relative order
*   Logical
    *   `all_of`\-`and`together all matchers
    *   `any_of`\-`or`together all matchers
    *   `anything`\- match anything, useful in composite matchers when you don't care about a particular value
    *   `is_not`,`not_`\- negate the matcher
*   Sequence
    *   `contains`\- exactly match the entire sequence
    *   `contains_inanyorder`\- match the entire sequence, but in any order
    *   `has_item`\- match if given item appears in the sequence
    *   `has_items`\- match if all given items appear in the sequence, in any order
    *   `is_in`\- match if item appears in the given sequence
    *   `only_contains`\- match if sequence's items appear in given list
    *   `empty`\- match if the sequence is empty
*   Dictionary
    *   `has_entries`\- match dictionary with list of key-value pairs
    *   `has_entry`\- match dictionary containing a key-value pair
    *   `has_key`\- match dictionary with a key
    *   `has_value`\- match dictionary with a value
*   Decorator
    *   `calling`\- wrap a callable in a deferred object, for subsequent matching on calling behaviour
    *   `raises`\- Ensure that a deferred callable raises as expected
    *   `described_as`\- give the matcher a custom failure description
    *   `is_`\- decorator to improve readability - see Syntactic sugar below

练习题

题目一

使用字典的方式对日志进行配置(yaml)

题目二

配置两个 handle 分别为对应 debug 级别日志和 info 级别日志,写入两个不同的日志文件。

题目三

日志的 formatter 要能展示

  1. 日志的时间
  2. 日志的文件名、函数名、行号
  3. 日志的级别

题目四

打印 error、exception、debug、info、warning、critical 级别的日志。