Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any Rails plugins that can generate models, views, etc using Cucumber scenarios?

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.

like image 340
johnmcaliley Avatar asked Dec 13 '10 20:12

johnmcaliley


2 Answers

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

like image 84
Steve Pallen Avatar answered Oct 17 '22 03:10

Steve Pallen


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."

like image 42
dkastner Avatar answered Oct 17 '22 05:10

dkastner