Cucumber Ruby - 模块中的一个步骤

时间:2017-06-29 17:26:30

标签: ruby cucumber

背景

我正在编写一个包含各种不同步骤的黄瓜库,以便在多个项目中使用,并尝试通过将步骤定义分为3个不同的模块来降低步骤定义的复杂性,适用于iOS,Android和Web - 特别是项目包括所有3个。

其中一个步骤库将包含一系列基于安全性的步骤,我希望在使用这些步骤之前将其明确包含在项目中。

将根据配置中指定的内容显式包含项目拆分库:

if $profile[:app_type] == 'android'
   World(ProjectSteps::Android)
else 
   if $profile[:app_type] == 'ios'
      World(ProjectSteps::IOS)
   else
      World(ProjectSteps::Web)
   end
end

这些不是要替换辅助方法,而是为了节省我们启动新项目的时间,并且还允许我们根据我们是否在测试Web,以不同的方式编写项目特定的步骤定义,本机iOS应用程序或原生Android应用程序,具有完全相同的功能,但又不同,需要不同的步骤定义

问题

在模块中定义步骤之后,即使模块未包含在" World"中,功能文件仍可以愉快地执行。像这样:World(CommonSteps::Security),这是你通常用来让黄瓜知道隐藏在模块内的辅助方法的方法。

When 'I provide my personal details' do
    select :title, 'Mr'
    fill :first_name, 'John'
    fill :last_name, 'Doe'

    unless $profile[:app_type] == 'web'
        click :progress
    end

    if $profile[:app_type] == 'android'
        fill :postcode, 'TE37ER'
        select :address, '1 Test Street'
        click :progress
        fill :occupation, 'Tester'
        fill :company, 'Test Company'
        click :progress
    else 
        fill :occupation, 'Tester'
        fill :company, 'Test Company'
        unless $profile[:app_type] == 'web'
           click :progress
        end
        fill :postcode, 'TE37ER'
        select :address, '1 Test Street'
        click :progress
    end
end

此步骤定义尝试同时测试3个应用,但它正在测试完全相同的功能,完全相同的方案和完全相同的功能。如果这被分成3个步骤定义,那么将来调试会更简单,但是相同的特征文件将能够用于它们中的每一个(这不是问题所在,因为有许多应用程序在Web和本机移动变体上共享完全相同的功能)。在我看来,这种步骤定义试图实现太多。

这更容易维护,尽管更多,因为它更简单:

module ProjectSteps::IOS
   When 'I provide my personal details' do
       select :title, $user[:title]
       fill :first_name, $user[:first_name]
       fill :last_name, $user[:last_name]
       click :progress
       fill :occupation, $user[:occupation]
       fill :company, $user[:company]
       click :progress
       fill :postcode, $user[:postcode]
       select :address, $user[:line1]
       click :progress
   end
end
module ProjectSteps::Android
   When 'I provide my personal details' do
       select :title, $user[:title]
       fill :first_name, $user[:first_name]
       fill :last_name, $user[:last_name]
       click :progress
       fill :postcode, $user[:postcode]
       select :address, $user[:line1]
       click :progress
       fill :occupation, $user[:occupation]
       fill :company, $user[:company]
       click :progress
   end
end
module ProjectSteps::Web
   When 'I provide my personal details' do
       select :title, $user[:title]
       fill :first_name, $user[:first_name]
       fill :last_name, $user[:last_name]
       fill :occupation, $user[:occupation]
       fill :company, $user[:company]
       fill :postcode, $user[:postcode]
       select :address, $user[:line1]
       click :progress
   end
end
When 'some thing that is the same across platforms' do
   # Some stuff
end

请记住,这是我试图实现的简单版本,并没有显示我试图解决的一些问题的完全复杂性。在这种情况下,我很可能会使用if / unless版本而不是拆分版本,但有一些案例会更加复杂,并且可能会分成3个部分。

我们还可以添加对我们在开发中发现的错误的静默检查,以确保这些不会消退,并且由于web,android和ios应用程序具有不同的错误,我们最终会得到一个大量的if / unless陈述。

我尝试了什么? - 我听到你问

真的关闭,或真的远离我。

GivenWhenThen在不同的模块中没有按预期工作,这就是我必须搜索我相信它们的方法的原因别名。

以下是生成的代码:

require_relative 'xss.rb'
require 'cucumber'

module CommonSteps
  module Security
    Cucumber::RbSupport::RbDsl.register_rb_step_definition(
        'I attempt to write a step definition that has to be included to work', 
        Proc.new { 
            # Some stuff here
        })
  end
end

完全注册步骤定义。但这是问题的一部分。如果模块已包含在我正在处理的项目的世界中,我只想注册此步骤定义。

这也意味着我们可以根据需要切换iOS和Android步骤的Web步骤,同时保持功能文件完全相同。 (是的,我知道if语句是一个东西,但是太多了,而且步骤定义变得非常复杂。)

修改

我想要实现的目标与“网络步骤”不同。我们过去看过的。没有通用的步骤显示代码,只有我们与开发团队一起与我们合作的业务达成一致的语言。由于我们所处理的很多项目都是跨平台的,因此我本质上试图实现能够切换使用哪种类型的步骤定义的东西。 - 如果您正在使用Chrome,请使用此步骤定义的Web版本,如果您使用的是iOS,请使用此步骤定义的iOS版本,还可以使用包含各种常规步骤的方法由文本提供支持,可以链接回我们的页面对象模型 - 保持场景完全基于业务。

Given I am on the "Personal Details" page # (generic)
When I provide my personal details # (non-generic, but Web, iOS and Android versions exist)
But leave the "First Name" field blank # (generic)
And I attempt to continue to the next page # (generic)
Then I should see a validation error for the "First Name" text box stating: "Please provide your first name" # (generic)

例如,如果验证是业务想要知道的,并且它是与业务达成一致的要求的一部分,那么是否有更多业务可理解的方式来传达该信息? - 这就是为什么我们要确保用户填写信息以便验证没有显示,但是如果他们不提供这些信息,我们也应该测试验证出现在需要它的场景中。

我们以这样的方式使用页面对象模型"个人详细信息"会在网址映射中找到:personal_details键。密钥可以传递给后续步骤,链​​接到包含Pages.personal_details密钥的:first_name方法。我们所有的项目都使用这个设置,它是我们的核心帮助器方法库的文档的一部分。

当我按照我建议的方式使用时,我试图实现的并不一定是不好的做法,但如果使用不当,可能会被这样使用。

1 个答案:

答案 0 :(得分:-1)

在Cucumber的历史上已经有很多次这样的事情已经完成了Cucumber本身曾经有过网络步骤,这些步骤已经被删除了。这些现在是宝石https://github.com/cucumber/cucumber-rails-training-wheels。您可能会从库中获得一些提示。

那就是说,我强烈建议不要编写步骤定义库。相反,我会编写一个可供步骤定义使用的方法库。粗略的例子可能有助于说明这一点。

假设您有一个非常复杂的登录应用程序的方法。您可以编写一个非常复杂的步骤定义来处理各种日志记录,例如

When I login (hugely complex regex to deal with things like ... # loads of code to deal with you params and regex's and make things work with lots of different scenarios

或者您可以编写类似

的方法

def login(as:, args={})

让人们在写东西时使用这种方法,例如

When 'I login' do
  login as: @i
end

When 'I login as Fred' do
  login as: create_or_find_user(firstname: 'Fred')
end

When 'I login as Fred with Jill's password' do
  login as: @fred, password: @jill.password
end

辅助方法提供了实用程序,可帮助您编写适合您的个人上下文的简单步骤定义。共享步骤定义限制您使用高度复杂且不具有任何特定于上下文的内容。

场景应该是特定于上下文的,并允许灵活的简单语言,这种语言特定于他们所属的个别世界的上下文。它们应该是关于为什么要做某事以及那是什么,并且对于如何做某事没有任何意义。根据定义,他们不会共享,因此根据定义也不会分享步骤定义。

通过拨打电话离开步骤定义后,您就进入了代码领域,代码在分享时非常有效

Cucumber已经吸取了教训,即共享步骤定义是一个非常糟糕的主意(参见http://aslakhellesoy.com/post/11055981222/the-training-wheels-came-off)。警惕重复过去的错误。