跳转到主要内容

pypom_form

项目描述

pypom_form

https://travis-ci.org/tierratelematics/pypom_form.svg?branch=develop https://requires.io/github/tierratelematics/pypom_form/requirements.svg?branch=develop https://readthedocs.org/projects/pypom_form/badge/?version=latest https://codecov.io/gh/tierratelematics/pypom_form/branch/develop/graph/badge.svg https://api.codacy.com/project/badge/Grade/0698c7aa2e164ee996518737aad7d6f4 Python 3

pypom_form 是一个基于 PyPOM 的包,它提供了一个基于声明式模式的表单交互页面对象。

pypom_form 旨在通过声明式模式模型,提高在需要与包含表单的页面对象交互时的 UI 和端到端测试自动化的开发者体验。

如果您来自像 SQLAlchemy、Dexterity (Plone) 或旧的 Archetypes (Plone) 这样的框架的使用经验,您应该已经熟悉这种模式:您只需定义一个带有模式的模型,您就可以通过保存或检索数据与您的模型交互。与 pypom_form 类似,模型是页面。

pypom_form 内部基于以下内容

它是如何工作的?

使用 pypom_form,您只需要

  • 实例化一个继承自 pypom_form 提供的 BaseFormPage 类的页面对象实例

  • 声明模式模型

然后您就可以通过仅使用表单与您的页面交互,驱动浏览器,只需输入

page.title = 'the title'
page.title

假设您的表单中有一个 标题 字段。

主要概念

您可以将 模式 概念视为一组命名属性(字段),它们将作为常规属性在 模型 上可用。

模式中的每个字段都定义了类型(例如:字符串、整数、浮点数、日期时间、日期、布尔值等),这定义了应用域级别给定字段的数据库类型。

字段有一个引用,可以强制定义或根据字段类型默认分配的小部件。pypom_form提供的小部件内部实现基于PyPOM的区域,因此小部件区域包装和管理包含小部件的DOM。

基本上,小部件通过序列化和反序列化将数据从应用域转换为浏览器域,反之亦然。

你可能认为小部件就像你在设置布尔属性为True或获取表单上的实际值时如何驱动浏览器:基本上这取决于小部件的实现。例如,你可能有一个复选框、是/否单选按钮或组合选择等,如果你想设置True,你驱动浏览器的方式会改变。对日期小部件也是如此。

你还需要处理复杂的小部件,例如

  • 引用小部件(例如:带有搜索、筛选等分层内容导航)

  • 高级多选小部件

  • 字典小部件(键值映射)

  • 等等

例如,假设你正在处理一个假设的高级单选选择字段,你可以访问由小部件区域提供的先进逻辑

page.getWidgetRegion('state').filter('virg').select('Virginia')

或访问验证错误消息、标签文本等。

为什么选择pypom_form

显然,你可以在自动化测试中使用纯selenium/splinter或使用传统的纯页面对象模型模式来驱动浏览器,但与pypom_form相比,你有以下优点

  • 一次编写、可重用方法,如果测试CMS框架非常实用

  • 页面和小部件逻辑的关注点分离

  • 声明性模式方法

  • 可重用模式和Widgets,无需代码重复

  • Widgets可以通过pypom_form与其他项目共享

  • 简单的基于自动生成的getter和setter的API

  • 通过基于PyPOM的区域小部件与高级小部件逻辑交互

  • 小部件隔离。所有元素查询都针对根区域运行,而不是页面根

  • 更简单的输入元素选择器,它们相对于区域小部件根是相对的

  • 模式表单改进了如何记录包含表单的页面(属性名称、类型、小部件、允许的词汇表等)。你需要知道的所有内容都在模式级别定义,整个图景一目了然

  • 如果你要测试基于colander/deform的应用程序(可能你正在测试基于Pylons Pyramid 的Python基于Web的应用程序),则支持重用现有的模式

  • 支持页面和模式继承

  • 易于测试具有相同数据模型、相同或不同选择器或小部件类型的单皮肤或多皮肤Web应用程序。因此,你可以重用所有页面对象类,它们被定义的方式,只有模式小部件选择器和类型发生改变

  • 小部件区域是PyPOM区域,因此如果你想访问小部件容器内的内部元素,生成的选择器将更简单,因为它们相对于小部件区域根。此外,也支持子/嵌套区域或动态区域

  • 使用应用域数据而不是浏览器域数据与你的模型交互。这更简单、更容易管理Python数据(例如,你设置12.9而不是‘12.9’,对于日期时间值也是如此,如datetime.now()

  • 支持链式调用,如page.set('title', 'the title')

  • 支持通过page.update(**values)通过在模式级别定义的顺序进行批量字段更新

  • 不要重新发明轮子。它基于现有的和广泛使用的组件,如plain PyPOM或Colander库

  • 如果你已经熟悉schema声明性模型,如SQLAlchemyArchetypes(Plone)、Dexterity(Plone)或表单库如deform,将提供相同的用户体验

  • 由于小部件实现基于区域,你可以在页面加载时简单地执行page.name = "the name",而不是在设置值之前必须调用等待方法:小部件能够在获取或设置数据之前等待小部件加载

  • 页面对象类更简单,代码更少,即使不同的测试工程师实现页面表单逻辑,也更具标准性:存在一种结构模式

此外

  • 100%测试覆盖率

  • 支持Python 2和3

  • 支持Splinter驱动程序(Selenium支持尚未可用)

  • 由于pytest-splinter,pytest设置就绪

代码示例

以下代码示例假设存在一个提供使用Splinter驱动程序构建的页面实例的导航固定装置,但你也可以根据PyPOM文档自己构建页面实例

Schema定义

import colander

from pypom_form.form import BaseFormPage


class BaseEditSchema(colander.MappingSchema):
    """ This is the base edit mapping common for all pages """

    name = colander.SchemaNode(
        colander.String(),
        selector=('id', 'name-widget'),
    )


class BaseEditPage(BaseFormPage):
    """ This is the base edit class """

    schema_factory = BaseEditSchema

假设你有一个可以与之交互的页面实例,你可以通过设置一个属性来操作上述页面

@pytest_bdd.when(pytest_bdd.parsers.parse(
    'I set {name} as name field'))
def fill_name(navigation, name):
    page = navigation.page
    page.name = name

你也可以定义具有扩展schema的其他页面,例如整数类型

class AnotherPageEditSchema(BaseEditSchema):

    duration = colander.SchemaNode(
        colander.Int(),
        missing=0,
        selector=('id',
                  'duration-widget'),
        validator=colander.Range(0, 9999))

但你也可以创建类似于colander.Bool或任何其他colander支持的类型。

并且测试

@pytest_bdd.when(pytest_bdd.parsers.cfparse(
    'I set {duration:Number} as Alarm duration',
    extra_types=dict(Number=int)))
def fill_alarm_duration(navigation, duration):
    page = navigation.page
    page.duration = duration

你可能会注意到在上面的例子中,你设置了一个整数持续时间而不是字符串。例如,你可以执行page.duration += 10

如果默认实现不符合你的应用程序上的实现(例如,非标准的复选框用于布尔小部件),你还可以在字段上定义自定义小部件,例如一个pretend MyBooleanWidget

mybool = colander.SchemaNode(
    colander.Bool(),
    missing=False,
    selector=(
        'id',
        'mybool-widget'
    ),
    pypom_widget=MyBoolWidget()
)

也支持链式调用(例如:设置标题,执行pretend提交方法,然后设置布尔值)

page.set('title', 'the title'). \
    .submit(). \
    .set('mybool', False)

或批量更新。所有更改都遵循schema级别的字段顺序

page.update(**{'title': 'the title', 'mybool': True})

可以使用updateraw_update在测试预条件创建中使用。假设你有一个具有复杂配置的通用给定步骤,你可以传递原始json数据,raw_update将负责将数据从浏览器模型(例如:字符串)转换为页面模型(字符串、整数、日期时间等)

@pytest_bdd.given(pytest_bdd.parsers.cfparse(
    'I have a CAN bus protocol configured with:\n{raw_conf:json}',
    extra_types=dict(json=json.loads)))
def create_can_protocol(navigation, base_url, raw_conf):
    """ create a can protocol
    """

    navigation. \
        visit_page('CANBusProtocolsPage'). \
        wait_for_full_spinner(). \
        click_add(). \
        raw_update(**raw_conf). \
        save(). \
        wait_for_success_pop_up_appears(). \
        click_on_ok_pop_up()

假设在.feature文件中指定了raw_conf的json格式,例如

@UI @edit @CANBusParameter
Scenario: Add a CAN bus parameter
  Given I am logged in as Administrator
  And I have a CAN bus protocol configured with:
      {"name": "The name",
       "baudrate": "250",
       ...
      }
  And ...

如上述代码示例所示,在页面加载时与表单交互之前不需要执行等待调用,因为每个小部件都能够等待其控制的输入元素就绪。等待逻辑已经在小部件级别上定义,并且你可以覆盖它们。

变更日志

0.3.1 (2017-10-24)

  • 添加了raw_dump方法来获取所有字段的序列化。

0.3.0 (2017-10-10)

  • 添加了dump方法来获取所有字段的大批量。

0.2.3 (2017-09-14)

  • 现在你可以覆盖自动生成的方法,并在其中调用super

0.2.2 (2017-07-03)

  • test_fields.py移动到正确的位置。

  • 如果已经存在,不要覆盖页面和区域上的updateraw_update方法。

0.2.1 (2017-06-01)

  • 更新电子邮件项目

0.2.0 (2017-01-24)

功能

  • 在组件基类实现中添加了 序列化反序列化 方法,以便进行高级使用。这样,您只需覆盖上述方法即可轻松实现复杂/组合组件(例如:从页面模型数据到浏览器或组件内部表示数据的中间转换)。

  • 为在需要实现复杂或组合组件时添加了 ObjectType 字段。

  • 添加了对 readonly 字段的支持。如果字段被标记为只读,则不会执行浏览器交互。

0.1.0 (2017-01-03)

功能

  • 将组件引用添加到组件区域,以便您可以从组件区域导航到组件并访问在模式中指定的组件选项。

  • 添加了 TextAreaWidget

文档

  • 改进了文档

0.0.1 (2016-12-22)

  • 初始版本

由以下支持