Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Small refactor: add `raw_input` method to make testing custom inputs easier #850

Closed
wants to merge 1 commit into from

2 participants

@nathanl

Added a raw_input method to return the input
after it has been initialized, but before it's
converted to html. This means that developers who
have created custom inputs can get an instance
of their input after it has been properly
initialized and test its instance methods.

Nathan Long Small refactor to make testing custom inputs easier
Added a `raw_input` method to return the input
after it has been initialized, but before it's
converted to html. This means that developers who
have created custom inputs can get an instance
of their input after it has been properly
initialized and test its instance methods.
409eaba
@nathanl

If there is already a better way to do this, please let me know. Here's how I'm using this in my application.

# I've customized StringInput
describe "StringInput instance methods" do
  before :all do
    @mothra = Mothra.new
  end

  before :each do
    # Use the helper method to properly instantiate an input for testing
    helper.semantic_form_for(@mothra, :url => '', as: 'monster') do |builder|
      @string_input = builder.send(:raw_input, :battle_cry)
    end
  end

  describe "class_for_string" do

    # We must validate the zip so that Mothra receives his power bill
    it "should give zip fields the 'zip' class" do
      @string_input.send(:class_for_string, 'zip_code').should eq('zip')
      @string_input.send(:class_for_string, 'billing_zip').should eq('zip')
    end

  # etc
  end
end
@justinfrench
Owner

@nathanl hi, this looks interesting... I've struggled with this a bit myself. I'd argue you should be building up your own instance (StringInput.new) and testing that. to_html is the interface for integration with a form, not for testing. Have you tried that instead? I know it's a fair chunk of work, but I'd be more excited about improving that that what you've proposed here, but I could be convinced :)

@nathanl

@justinfrench - I started out trying to do StringInput.new, but quickly became confused about how to pass all the parameters it needed. What I like about what I have above is being able to do the familiar semantic_form_for and let it do the instantiation. But I'm not adamant about this approach.

When you say "I'd be more excited about improving that," I'm not sure exactly what you mean. Are you thinking we could make a simpler way to instantiate the input?

@justinfrench
Owner

@nathanl Yeah, I'm wondering if we could change the instantiation to accept either then N arguments we currently require or collapse all/some of them down into a single "context" container object, and that Formtastic provide a suitable mock object to test against, meaning that the tests could be updated to something like:

i = StringInput.new(Formtastic::MockContext)

I haven't really thought much further than that. Formtastic definitely needs access to everything we pass in, but I'm not too fussed about how.

Actually, if we could just provide great mocks for each of the args passed in, that'd be a great start. We really need this to improve Formtastic's own internal tests, which are out of control.

@nathanl

@justinfrench - I spent some time looking into this, but it appears to be a bit more than I can bite off right now. In the process, however, I discovered that I don't need my patch in order to test my custom StringInput instances; I can just do this:

      @string_input = StringInput.new(nil, ActionView::Base.new, @mothra, 'monster', :battle_cry, {})

As you can see, I'm not passing in an instance of Formtastic::FormBuilder as the first argument. This is because my particular input doesn't need to use it. (I almost could have gotten away with omitting the template, too, but I was calling one Rails view helper method.)

It seems to me that the hardest part in creating a Formtastic::MockContext is creating a mock Formtastic::MockFormBuilder. If someone can do that, I can try using that to make the MockContext and update Formtastic::Inputs::Base#initialize to accept it.

In the meantime, I do think this patch provides the easiest way to get a fully-initialized input for prodding in tests. Any chance you'd reconsider merging it?

@justinfrench
Owner

Closing due to lack of activity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 15, 2012
  1. Small refactor to make testing custom inputs easier

    Nathan Long authored
    Added a `raw_input` method to return the input
    after it has been initialized, but before it's
    converted to html. This means that developers who
    have created custom inputs can get an instance
    of their input after it has been properly
    initialized and test its instance methods.
This page is out of date. Refresh to see the latest.
Showing with 7 additions and 3 deletions.
  1. +7 −3 lib/formtastic/helpers/input_helper.rb
View
10 lib/formtastic/helpers/input_helper.rb
@@ -232,16 +232,20 @@ module InputHelper
# @todo Many many more examples. Some of the detail probably needs to be pushed out to the relevant methods too.
# @todo More i18n examples.
def input(method, options = {})
+ raw_input(method, options).to_html
+ end
+
+ protected
+
+ def raw_input(method, options = {})
options = options.dup # Allow options to be shared without being tainted by Formtastic
options[:as] ||= default_input_type(method, options)
klass = input_class(options[:as])
- klass.new(self, template, @object, @object_name, method, options).to_html
+ klass.new(self, template, @object, @object_name, method, options)
end
- protected
-
# First try if we can detect special things like :file. With CarrierWave the method does have
# an underlying column so we don't want :string to get selected.
#
Something went wrong with that request. Please try again.