是否有任何 Rails 插件可以使用 Cucumber 场景生成模型、视图等?

发布于 2024-10-07 13:46:39 字数 2776 浏览 3 评论 0原文

我计划创建一个插件,该插件将根据 Cucumber 场景生成应用程序代码,但我想确保我不会在这里重新发明轮子。有谁知道有一个可以与 Cucumber 一起使用并生成模型、控制器和视图的插件吗?

只是一些关于我正在尝试做的事情的背景知识,以防这没有意义。当我创建一个新应用程序时,我的工作流程如下:

  1. 在我的白板上勾画出 2 种类型的高级设计。一个显示模型和关系,另一个显示布局、表单等的一些原始屏幕。

  2. 根据高级设计(但更细粒度)编写 Cucumber 场景。其中许多步骤只是描述我将在特定视图上看到的内容,并概述应用程序的流程。我发现在开始编码之前创建我能想到的所有场景比一次只做一个并在编写每个场景后进行编码要好。

  3. 我运行黄瓜场景并查看第一个失败并从那里开始编码。在此步骤之前,我通常会进行一些额外的设置,以根据我的喜好配置我的 Rails 应用程序,并包含我知道我会使用的 gem。我还找到了运行功能文件的逻辑顺序,因为有些功能文件依赖于其他功能文件。显然是从身份验证之类的事情开始。

  4. 然后我使用 Rails 生成器(脚手架或模型)来帮助我创建传递场景所需的代码。我更改了一些生成器模板来提供我想要的内容。

  5. 然后,如有必要,我会调整生成的代码。大多数情况下,这涉及在模型中设置关系、处理视图中的关联以及脚手架无法提供的任何其他非标准功能。

  6. 如果需要,我运行迁移

  7. 然后我重新运行我的场景并重复 4-6 中的任何步骤,直到场景通过。

  8. 重复步骤 4-7,直到所有场景都通过。

我可能是错的,但我认为很多人可能使用与此类似的方法。让我烦恼的是,我发现编写场景和生成/调整代码之间有很多重复。我希望能够使用黄瓜场景生成应用程序的骨架,并使用步骤定义来帮助我自定义生成的内容。这是一个示例:

Scenario: MODEL widget exists
  Given a widget model exists
  Then it should belong to a "manufacturer"
  And it should have a "quantity:integer" field
  And it should validate the presence of "quantity"
  And it should have many "wadgets"
  And it should accept nested attributes for "wadgets"
  #etc...

Scenario: VIEW new widget page
  Given I am on the new widgets page
  Then I should see a "quantity" field
  And I should see a "wadgets:name" nested field
  And I should see a button with text "Save Widget"

Scenario: CONTROLLER widget is created
  Given a new widget is created
  Then I should be on the widgets page

这将生成如下代码:

#FROM SCENARIO 1
class Widget < ActiveRecord::Base
  has_many :wadgets
  belongs_to :manufacturer
  validates_presence_of :quantity
  accepts_nested_attributes_for :wadgets
end

#FROM SCENARIO 1      
class CreateWidget < ActiveRecord::Migration
  def self.up
    create_table :widgets do |t|
      t.integer :quantity, :null=>false
      t.integer :manufacturer_id

      t.timestamps
    end
  end

  def self.down
    drop_table :widgets
  end
end

#FROM SCENARIO 2
#new.html.haml (using formtastic helpers)
=semantic_form_for(@widget) do |f|
  = f.inputs do
    = f.input :quantity
    = f.semantic_fields_for :wadgets do |wadget|
      = location.input :name
  = f.buttons 
    =f.commit_button "Save Widget"

#FROM SCENARIO 3 (using inherited resources)
class WidgetsController < InheritedResources::Base
  def create
    create!{ widget_urls }
  end
end

此时这只是伪代码,但我认为在 Cucumber 场景中定义应用程序,然后根据这些场景中的内容生成代码将是一个真正的节省时间的方法。这将允许您同时创建测试和编写代码。您不必为脚手架生成器命令行键入所有字段,它会自动设置关联并在视图中创建正确的字段类型。此外,它还允许您将整个功能设计保存在一个文件中。使用这种方法,您将首先在场景上运行生成器,然后在生成后运行 Cucumber 测试。如果设置正确,一切都会一次性通过,并且您将拥有一个可以自定义的非常可靠的原型。

有没有类似这种测试和测试的插件?一代组合?

感谢您花时间阅读本文。我知道它有点长。

I am planning on creating a plugin that will generate app code based on Cucumber scenarios, but I want to make sure I am not reinventing the wheel here. Is anyone aware of a plugin that works with Cucumber and generates models, controllers and views?

Just a little background on what I am trying to do in case this doesn't make sense. When I create a new application here is my workflow:

  1. Sketch out 2 types of high level designs on my whiteboard. 1 that shows models and relationships and another that shows some primitive screens for layouts, forms, etc.

  2. Write cucumber scenarios based on the high level design (but finer grained). Many of these steps just describe what I will see on a particular view and also outline the flow of the application. I find that creating all the scenarios I can think of before I start coding is better than doing one at a time and coding after writing each scenario.

  3. I run the cucumber scenarios and look at the first failure and start coding from there. I usually have some additional setup before this step to configure my Rails app to my preferences and include gems that I know I will use. I also find a logical order to run my feature files since some are dependent on others. Obviously starting with things like authentication.

  4. Then I use Rails generators (scaffold or just model) to help me create the code I need to pass a scenario. I alter some of the generator templates to give me what I want.

  5. Then I tweak the generated code if necessary. Most of the times this involves setting up relationships in the model, working with the associations in the views, and any other non-standard functionality that scaffolding can't provide.

  6. I run my migrations if necessary

  7. Then I rerun my scenarios and repeat any steps in 4-6 until the scenario passes.

  8. Repeat steps 4-7 until all scenarios pass.

I may be wrong, but I think alot of people probably use an approach similar to this. The thing that annoys me is that I see alot of duplication between writing the scenarios and generating/tweaking code. I want to be able to generate the skelaton of my app with my cucumber scenarios and use the step definitions to help me customize what is generated. Here is an example:

Scenario: MODEL widget exists
  Given a widget model exists
  Then it should belong to a "manufacturer"
  And it should have a "quantity:integer" field
  And it should validate the presence of "quantity"
  And it should have many "wadgets"
  And it should accept nested attributes for "wadgets"
  #etc...

Scenario: VIEW new widget page
  Given I am on the new widgets page
  Then I should see a "quantity" field
  And I should see a "wadgets:name" nested field
  And I should see a button with text "Save Widget"

Scenario: CONTROLLER widget is created
  Given a new widget is created
  Then I should be on the widgets page

This would generate code like so:

#FROM SCENARIO 1
class Widget < ActiveRecord::Base
  has_many :wadgets
  belongs_to :manufacturer
  validates_presence_of :quantity
  accepts_nested_attributes_for :wadgets
end

#FROM SCENARIO 1      
class CreateWidget < ActiveRecord::Migration
  def self.up
    create_table :widgets do |t|
      t.integer :quantity, :null=>false
      t.integer :manufacturer_id

      t.timestamps
    end
  end

  def self.down
    drop_table :widgets
  end
end

#FROM SCENARIO 2
#new.html.haml (using formtastic helpers)
=semantic_form_for(@widget) do |f|
  = f.inputs do
    = f.input :quantity
    = f.semantic_fields_for :wadgets do |wadget|
      = location.input :name
  = f.buttons 
    =f.commit_button "Save Widget"

#FROM SCENARIO 3 (using inherited resources)
class WidgetsController < InheritedResources::Base
  def create
    create!{ widget_urls }
  end
end

This is just psuedo at this point, but I think it would be a real time-saver to define your application in Cucumber scenarios and then generate the code based on what is in these scenarios. This would allow you to create tests and write code at the same time. And you would not have to type out all of the fields for the scaffold generator command line, and it would automatically set the associations and create the proper fields types in the view. Also, it would allow you to keep your entire feature design in one file. Using this approach, you would run the generator first on the scenario and then run the cucumber tests after the generation. If it was set up correctly, everything would pass the first time and you would have a pretty solid prototype that you could customize.

Are there any plugins that resemble this kind of testing & generation combo?

And thanks if you took the time to read this.. I know it was a bit long.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

雅心素梦 2024-10-14 13:46:39

几天前我也有同样的想法。然而,经过更多思考,我放弃了从特征文件生成模型的想法。相反,我正在使用 dsl,它使用 dsl 中的 Rails 生成器生成模型/支架/资源。

在我完成这项工作后,我正在考虑挂钩生成器来创建基于 dsl 的功能文件。

我有一个正在运行的峰值,它接受以下输入:

application :test do
  model :survey do
    attribute :name, :string
    has_many :questions
  end
  model :question do
    has_many :options
    has_many :answers
    belongs_to :survey
    attribute :body, :string
  end
  model :option do
    belongs_to :question
    attribute :body, :string
    attribute :selector, :string

  end
  model :result do
    belongs_to :survey
    has_many :answers
  end
  model :answer do
    belongs_to :result
    belongs_to :question
    attribute :value, :string
  end
  gen
end

并打印以下输出:

rails new test
cd test
rails generate model survey name:string 
rails generate model question survey_id:integer body:string 
rails generate model option question_id:integer body:string selector:string 
rails generate model result survey_id:integer 
rails generate model answer result_id:integer question_id:integer value:string 
Updating class: Survey
  has_many:questions
Updating class: Question
  belongs_to:survey
  has_many:options
  has_many:answers
Updating class: Option
  belongs_to:question
Updating class: Result
  belongs_to:survey
  has_many:answers
Updating class: Answer
  belongs_to:result
  belongs_to:question

Steve

I had the same idea a couple of days ago. However, after thinking about it some more, I abandon the ideas of generating the models from the feature files. Instead, I'm playing with a dsl that generates the models/scaffold/resources using the rails generator from the dsl.

After I get this working, I was thinking of hooking in generators to create feature files based on the dsl.

I have a spike running that takes the following input:

application :test do
  model :survey do
    attribute :name, :string
    has_many :questions
  end
  model :question do
    has_many :options
    has_many :answers
    belongs_to :survey
    attribute :body, :string
  end
  model :option do
    belongs_to :question
    attribute :body, :string
    attribute :selector, :string

  end
  model :result do
    belongs_to :survey
    has_many :answers
  end
  model :answer do
    belongs_to :result
    belongs_to :question
    attribute :value, :string
  end
  gen
end

and prints the following output:

rails new test
cd test
rails generate model survey name:string 
rails generate model question survey_id:integer body:string 
rails generate model option question_id:integer body:string selector:string 
rails generate model result survey_id:integer 
rails generate model answer result_id:integer question_id:integer value:string 
Updating class: Survey
  has_many:questions
Updating class: Question
  belongs_to:survey
  has_many:options
  has_many:answers
Updating class: Option
  belongs_to:question
Updating class: Result
  belongs_to:survey
  has_many:answers
Updating class: Answer
  belongs_to:result
  belongs_to:question

Steve

娇纵 2024-10-14 13:46:39

我认为你在这里使用黄瓜并不符合其预期。

我假设我们都同意 Cucumber 功能应该描述客户想要看到的某些功能 - 它们基本上将故事卡(需求)转换为可运行的测试。这些故事不应该关心模型、控制器和视图的实现。它应该测试诸如“当我单击按钮 X 时,我应该被带到页面 Y,并且我的提交应该被批准”之类的内容。这只是一项模拟用户与站点之间交互的大型集成测试。在您的示例中,您在页面上查找特定字段,可以通过说“当我用 5 填写数量字段时”来隐式检查该字段。

为了测试模型的行为以及它们如何与业务逻辑交互,您最好使用 RSpec 或 Test::Unit - 编写测试和进行模拟/存根要容易得多。我确信有一些插件可以为模型中的每个字段/关系生成 RSpec 测试。 rspec-rails 生成器已经为您完成了大部分工作,例如 rails generated rspec:scaffold Post

我认为对 cucumber-rails 的最佳改进是将模型和控制器测试留给 RSpec,但是然后能够使用生成的 Cucumber 功能为给定资源生成标准 CRUD 操作,例如“用户应该能够创建 X”。

I think your use of cucumber here is not as it was intended.

I'm assuming we all agree that cucumber features should describe certain features that the customer wants to see - they basically translate story cards (requirements) into runnable tests. These stories should not be concerned with the implementation of your models, controllers, and views. It should test things like "when I click button X, I should be taken to page Y, and my submission should be approved." It's just one big integration test that simulates the interaction between the user and your site. In your example, you look for specific fields on the page, which could be implicitly checked by saying "when I fill in the quantity field with 5."

For testing the behavior of models and how they interact with business logic, you would be better off using RSpec or Test::Unit - it's much easier to write tests and do mocking/stubbing. I'm sure there are plugins that generate RSpec tests for each field/relationship in your model. The rspec-rails generators already to most of the work for you, e.g. rails generate rspec:scaffold Post

I think the best improvement to cucumber-rails would be to leave the model and controller testing to RSpec, but then be able to generate standard CRUD actions for a given resource with generated cucumber features, like "A user should be able to create an X."

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文