简体   繁体   English

黄瓜红宝石-模块内的步骤

[英]Cucumber Ruby - A step within a module

Background 背景

I'm writing both a cucumber library with a variety of different steps to use across multiple projects and attempting to reduce complexity of step definitions by splitting them into 3 different modules, for iOS, Android and Web - specifically projects that include all 3. 我正在编写一个黄瓜库,该库具有用于多个项目的各种不同步骤,并试图通过将步骤定义分成iOS,Android和Web的3个不同模块(特别是包含全部3个项目的3个模块)来降低步骤定义的复杂性。

One of the steps libraries will include a load of security-based steps that I want to explicitly include into a project before using the steps. 步骤库之一将包含大量基于安全性的步骤,在使用这些步骤之前,我想将这些步骤明确包含在项目中。

The project split library will be explicitly included depending on what has been specified in the configuration: 根据配置中指定的内容,将显式包括项目拆分库:

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

These are not to replace the helper methods, but to save us time when starting up on new projects and will also allow us to have project specific step definitions written in different ways depending on whether we are testing the web, native iOS app or native Android app built to have the exact same functionality, but are different enough to require a different step definition 这些不是替换辅助方法,而是为我们节省了启动新项目时的时间,并且还使我们能够根据我们是在测试网络,本机iOS应用还是本机Android以不同的方式编写特定于项目的步骤定义应用程序具有完全相同的功能,但差异很大,需要不同的步骤定义

The Issue 问题

After defining the step within a module, the feature files can still execute this happily, even if the module has not been included in the "World" like this: World(CommonSteps::Security) , which is what you would usually use to let cucumber know about helper methods hidden away inside of modules. 在模块中定义了步骤之后,即使没有将模块包含在“世界”中,特征文件仍然可以愉快地执行: 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

This step definition is trying to test 3 apps at once, but it is testing the exact same functionality, the exact same scenarios and the exact same features. 此步骤定义试图一次测试3个应用程序,但它测试的是功能完全相同,方案完全相同且功能相同的功能。 If this was split into 3 step definitions, then it would be simpler to debug in the future, but the same feature file would be able to be used for each of them (which isn't out of the question, as there are many apps that share the exact same functionality across web and native mobile variants). 如果将其分为3个步骤定义,那么将来调试起来会更简单,但是每个功能定义都可以使用相同的功能文件(这并不是没有问题的,因为有许多应用程序在网络和本地移动版本中具有完全相同的功能)。 In my opinion, this type of step definition is trying to achieve too much. 我认为,这种类型的步骤定义试图实现太多目标。

This would be easier to maintain despite being more, as it is simpler: 尽管更多,但维护起来更容易,因为它更简单:

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

Bear in mind, this is a simple version of what I'm trying to achieve, and doesn't show the full complexity of some of the issues that I'm trying to resolve. 请记住,这是我要实现的目标的简单版本,并且没有显示出我要解决的某些问题的全部复杂性。 In this case, I would most likely go with the if / unless version rather than the split, but there are a few cases that are drastically more complex, and would benefit from being split into 3 sections. 在这种情况下,我很可能会使用if / unless版本而不是拆分,但是有些情况会更加复杂,将拆分成3个部分会受益。

We could also add silent checks for bugs that we found in development to make sure that these don't regress, and as the web, android and ios apps feature different bugs, we'd end up with a large amount of if / unless statements. 我们还可以添加对开发中发现的错误的静默检查,以确保这些错误不会消退,并且由于网络,Android和ios应用程序具有不同的错误,因此最终会产生大量的if / unless语句。

What have I tried? 我尝试了什么? - I hear you ask -我听你问

Well I'm either really close, or really far off. 好吧,我要么真的很接近,要么真的很遥远。

Given , When and Then don't work as expected when within a different module, which is why I had to search for the method that I believe them to be an alias of. GivenWhenThen不同的模块,这就是为什么我不得不寻找,我相信他们是一个别名的方法中时不能按预期工作。

Here is the resulting code: 这是结果代码:

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

Registers the step definition absolutely fine. 绝对正确地注册步骤定义。 But that's part of the problem. 但这是问题的一部分。 I only want to register this step definition if the module has been included in the world of the project that I'm working on. 如果该模块已包含在我正在处理的项目中,那么我只想注册此步骤定义。

It would also mean that we can switch out the Web steps for the iOS and Android steps if we need to, while keeping the feature files the exact same. 这也意味着我们可以根据需要在iOS和Android步骤中切换Web步骤,同时保持功能文件完全相同。 (Yes I know if statements are a thing, but too many, and step defs get complicated really fast.) (是的,我知道if陈述的事情,但也嫌多,一步DEFS得到真快复杂。)

Edit 编辑

What I'm trying to achieve is not like the "Web steps" that we have seen in the past. 我想要实现的目标与我们过去看到的“ Web步骤”不同。 No generic steps show code and are only in the language that we have agreed with the businesses that we work with, alongside the development team. 没有通用步骤显示代码,并且仅使用我们与开发团队一起与合作企业达成一致的语言。 As a lot of the projects we work on are cross platform, I'm in essence trying to achieve something that will switch which type of step definition is being used. 由于我们从事的许多项目都是跨平台的,因此我实质上是在尝试实现一些可以切换使用哪种类型的步骤定义的项目。 - if you're using Chrome, use the Web version of this step definition, if you're using iOS, use the iOS version of this step definition, but also a means to include a variety of generic steps that are powered by text, which can link back to our page object model - keeping the scenarios completely business-based. -如果您使用的是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)

For example, and if the validation is something that the business wants to know is working, and it'a part of the requirements that have been agreed with the business, is there a more business understandable way to communicate that information? 例如,如果验证是企业想要知道的事情正在起作用,并且已经与企业达成协议,那么它是与企业达成共识的需求的一部分,是否有一种更易理解的信息交流方式? - the why in this, is that we want to make sure the user fills in the information so that the validation does not show, but in the case that they don't provide that information, we should be also testing for the validation appearing in scenarios where it is needed. -这样做的原因是,我们要确保用户填写信息以使验证不显示,但在他们不提供该信息的情况下,我们也应该测试是否出现在验证中需要它的场景。

We use the page object model in such a way that "Personal Details" will find the :personal_details key in the urls map. 我们使用页面对象模型的方式是“个人详细信息”将在url映射中找到:personal_details键。 The key can be passed down to the next steps, linking to the Pages.personal_details method, which contains the :first_name key. 该密钥可以向下传递到下一步,链接到包含:first_name密钥的Pages.personal_details方法。 All of our projects use this setup, and it's part of the documentation that goes with our core helper method library. 我们所有的项目都使用此设置,它是核心帮助程序方法库附带的文档的一部分。

What I'm trying to achieve is not necessarily bad practice when used in the way that I'm suggesting, but could, if used incorrectly, be used as such. 当我按照建议的方式使用时,我试图实现的目标并不一定是错误的做法,但是如果使用不正确,则可以这样使用。

There have been quite a few times in Cucumber's history when things like this have been done Cucumber itself used to have web steps, which was removed some time ago. 在Cucumber的历史中已经进行过很多次这样的事情了,Cucumber本身曾经具有Web步骤,该步骤已在不久前被删除。 These are now in a Gem https://github.com/cucumber/cucumber-rails-training-wheels . 这些现在位于Gem https://github.com/cucumber/cucumber-rails-training-wheels中 You might get some tips from that for your library. 您可能会从图书馆中获得一些提示。

That said, I would strongly recommend not writing a library of step definitions. 也就是说,我强烈建议您不要编写步骤定义库。 Instead I would write a library of methods that can be used by step definitions. 相反,我将编写一个可以供步骤定义使用的方法库。 A crude example might help illustrate this. 一个简单的例子可能有助于说明这一点。

Lets say you have a really complex method for logging in to an application. 可以说您有一种非常复杂的方法来登录应用程序。 You could write a really commplicated step definition that deals with all sorts of logging in things such as 您可以编写一个非常复杂的步骤定义,以处理各种登录操作,例如

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

or you could write a method like 或者你可以写一个像

def login(as:, args={})

and let people use this method when they write stuff eg 让人们在写东西时使用这种方法,例如

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

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

or 要么

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

The helper methods provide utility to help you write simple step definitions that are appropriate to your individual context. 帮助程序方法提供实用程序,可帮助您编写适合您的单个上下文的简单步骤定义。 A shared step definition restricts you to using something that is highly complex and cannot have anything context specific. 共享步骤定义将限制您使用高度复杂且不能具有任何特定于上下文的内容。

Scenarios should be context specific and allow flexible simple language that is specific to the context of the individual World they are part of. 场景应特定于上下文,并允许使用灵活的简单语言,这些特定语言应针对它们所属的各个世界。 They should be all about Why something is being done and What that thing is, and have nothing about How something is being done. 他们应该只关注为什么要做某事,该做什么是什么,而不应该了解某事的完成方式。 By definition they do not share and so by definition neither to step definitions. 根据定义,它们不共享,因此,根据定义,它们也不共享步骤定义。

Once you've left a step definition by making a call you are into the realm of code, and code is really effective at sharing 通过调用离开步骤定义后,您便进入了代码领域,并且代码在共享方面确实非常有效

Cucumber has learnt the lesson that shared step definitions are a really bad idea (see http://aslakhellesoy.com/post/11055981222/the-training-wheels-came-off ). 黄瓜学到了一个教训,那就是共享步骤定义是一个非常糟糕的主意(请参阅http://aslakhellesoy.com/post/11055981222/the-training-wheels-came-off )。 Be wary of repeating past mistakes. 谨防重复过去的错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM