Fields_for view spec

For some reason I can’t figure out how to make the fields_for tags
render in the trivial example below; however, it works in the browser.
What does #build_association do that my stubbed method does not
replicate? (Or is that even the issue?)

I appreciate the insight. Thanks! Matt Smith

#spec/views/assets/new.html.erb_spec.rb
describe “assets/new.html.erb” do
let(:asset) { mock_model(“Asset”).as_new_record.as_null_object }
let(:owner) { mock_model(“Owner”).as_new_record.as_null_object }

before(:each) do
asset.stub(:owner => owner)
assign(:asset, asset)
end

it “renders new asset form” do
render

assert_select "form", :action => assets_path, :method => "post" do
  assert_select "input#asset_name", :name => "asset[name]" # passes
  assert_select "input#asset_owner_attributes_name", :name =>

“asset[owner_attributes][name]” # fails!
end
end
end

#app/views/assets/_form.html.erb
<%= form_for(@asset) do |f| %>

<%= f.label :name %>
<%= f.text_field :name %>

<%= f.fields_for :owner do |owner_fields| %>


<%= owner_fields.label :name %>
<%= owner_fields.text_field :name %>

<% end %>
<%= f.submit %>
<% end %>

On 4 April 2011 02:00, Matt S. [email protected] wrote:

let(:owner) { mock_model(“Owner”).as_new_record.as_null_object }
assert_select “input#asset_name”, :name => “asset[name]” # passes
<%= f.label :name %>

<%= f.submit %>
<% end %>

In similar view specs, I’ve stubbed #owner_attributes= on the ‘assets’
mock. I think Rails’ nested form/assignment implementation does a
check on the existence of this method to make sure that the Asset
model does indeed accept nested assignment for that attribute.

Chris

Chris M. wrote in post #990804:

On 4 April 2011 02:00, Matt S. [email protected] wrote:

let(:owner) { mock_model(“Owner”).as_new_record.as_null_object }
assert_select “input#asset_name”, :name => “asset[name]” # passes
<%= f.label :name %>

<%= f.submit %>
<% end %>

In similar view specs, I’ve stubbed #owner_attributes= on the ‘assets’
mock. I think Rails’ nested form/assignment implementation does a
check on the existence of this method to make sure that the Asset
model does indeed accept nested assignment for that attribute.

Chris

Thanks Chris,

I’ll give that a try and post my solution.

Matt

Matt S. wrote in post #991032:

In similar view specs, I’ve stubbed #owner_attributes= on the ‘assets’
mock. I think Rails’ nested form/assignment implementation does a
check on the existence of this method to make sure that the Asset
model does indeed accept nested assignment for that attribute.

Chris

Thanks Chris,

I’ll give that a try and post my solution.

Matt

Well it appears I am still stumped…

If I change Asset to:
class Asset < ActiveRecord::Base
belongs_to :owner
accepts_nested_attributes_for :owner

def owner_attributes=(attributes)
#test
end
end

The new form works in the browser, but not in the test. If I change my
before block to the following (and eliminate the def owner_attributes=),
the test still fails.

before(:each) do
asset.stub(:owner => owner)
asset.stub(:owner_attributes=).with(anything()) # new line
assign(:asset, asset)
end

Any thoughts? Am I stubbing this incorrectly?

Much thanks, Matt

On 2 May 2011 00:58, Matt S. [email protected] wrote:

accepts_nested_attributes_for.)
before(:each) do
end
<%= f.fields_for :owner do |owner_fields| %>

class AssetController < ApplicationController
def new
@asset = Asset.new
@asset.build_owner
end
end

Can you get the contents of ‘response.body’ from inside that spec
example, so we can see what’s being rendered when the spec is run?

Chris

  1. Is there a way to make this trivial example spec to pass? (If so,
    how?)

  2. Advice: I would like to write more view specs, especially on views
    for models with more complex relationships. Is it worth it?

(The reason I ask this somewhat rhetorical question is because I cannot
find any examples of this on the web and have tried about everything I
can think of, but I still can’t get view specs containing nested model
forms to pass, using fields_for on models with
accepts_nested_attributes_for.)

Much thanks!!!

Matt Smith

#spec/views/assets/new.html.erb_spec.rb
describe “assets/new.html.erb” do
let(:asset) { mock_model(“Asset”).as_new_record.as_null_object }
let(:owner) { mock_model(“Owner”).as_new_record.as_null_object }

before(:each) do
asset.stub(:owner => owner)
assign(:asset, asset)
end

it “renders new asset form with owner attributes” do
render
assert_select “form[method=post][action=?]”, assets_path do
assert_select
“input[type=text][name=‘asset[owner_attributes][name]’]”
end
end
end

#app/views/assets/new.html.erb
<%= form_for(@asset) do |f| %>

<%= f.label :name %>
<%= f.text_field :name %>

<%= f.fields_for :owner do |owner_fields| %>


<%= owner_fields.label :name %>
<%= owner_fields.text_field :name %>

<% end %>
<%= f.submit %>
<% end %>

Where the relationship will be the following…

class Asset < ActiveRecord::Base
has_one :owner
accepts_nested_attributes_for :owner
end

class Owner < ActiveRecord::Base
belongs_to :asset
end

class AssetController < ApplicationController
def new
@asset = Asset.new
@asset.build_owner
end
end

Chris M. wrote in post #996234:

On 2 May 2011 00:58, Matt S. [email protected] wrote:

accepts_nested_attributes_for.)
before(:each) do
end
<%= f.fields_for :owner do |owner_fields| %>

class AssetController < ApplicationController
def new
@asset = Asset.new
@asset.build_owner
end
end

Can you get the contents of ‘response.body’ from inside that spec
example, so we can see what’s being rendered when the spec is run?

Chris

Thanks Chis,

Here is the rendered content:

New asset

Name

Owner

Back

As you can see nothing comes up for the fields_for :owner block. I have
tried stubbing owner_attributes and owner_attributes=, as your
previously suggested, but I saw no change. It does seem that I need to
mock something else on the assets model to get this to work. I have
started combing through the the action_view source code but have not run
into anything to get me on the right trail yet, although I have learned
a lot about other things!

If you want to see everything you can do a ‘git clone
git://github.com/matthewcalebsmith/asset_owner.git’ to get the whole
mock project, in case I have left out any pertinent details.

Thank you!

Matt Smith

Chris M. wrote in post #996461:

On 3 May 2011, at 15:05, Matt S. wrote:

@asset.build_owner
Here is the rendered content:
<input id=“asset_name” name=“asset[name]” size=“30” type=“text”

a lot about other things!

If you want to see everything you can do a ‘git clone
git://github.com/matthewcalebsmith/asset_owner.git’ to get the whole
mock project, in case I have left out any pertinent details.

The following patch makes the example pass for me. The key change was to
take out the #as_null_object call on your mock models. That seems to be
interfering with Action View’s detection of nested attributes
acceptance, but I’m not sure exactly why.

Chris

From 0829776dd45b3a0c57134f7af5d4f21feebe5fd7 Mon Sep 17 00:00:00 2001
From: Chris M. [email protected]
Date: Tue, 3 May 2011 18:11:40 +0100
Subject: [PATCH] Fix stubbing for nested form in assets/new.


spec/views/assets/new.html.erb_spec.rb | 11 +++++++±–
1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/spec/views/assets/new.html.erb_spec.rb
b/spec/views/assets/new.html.erb_spec.rb
index b8845fe…9bf3b26 100644
— a/spec/views/assets/new.html.erb_spec.rb
+++ b/spec/views/assets/new.html.erb_spec.rb
@@ -1,13 +1,18 @@
require ‘spec_helper’

describe “assets/new.html.erb” do

  • let(:asset) { mock_model(“Asset”).as_new_record.as_null_object }
  • let(:owner) { mock_model(“Owner”).as_new_record.as_null_object }
  • let(:asset) { mock_model(“Asset”).as_new_record }

  • let(:owner) { mock_model(“Owner”).as_new_record }

    before(:each) do
    asset.stub(:owner => owner)

  • asset.accepts_nested_attributes_for :owner
  • asset.stub(:name)

  • asset.stub(:owner_attributes=)

  • owner.stub(:name)

  • assign(:asset, asset)

  • end

    it “renders new asset form with owner attributes” do

    1.7.4.4

Thanks Chris,

I guess it ended up being like you said at the beginning. It is good to
know about the as_null_object behavior, which seems a bit odd, but I
have too much work to worry about it right now. Definitely reinforces
the idea of writing as little code as possible to make it pass, and only
then refactor. If I had truly done that I probably would have caught
this.

Thank you for your time. I hope this also saves someone else a headache
in the future.

Thanks again,

Matt Smith

On 3 May 2011, at 15:05, Matt S. wrote:

@asset.build_owner
Here is the rendered content:
<input id=“asset_name” name=“asset[name]” size=“30” type=“text”

a lot about other things!

If you want to see everything you can do a ‘git clone
git://github.com/matthewcalebsmith/asset_owner.git’ to get the whole
mock project, in case I have left out any pertinent details.

The following patch makes the example pass for me. The key change was to
take out the #as_null_object call on your mock models. That seems to be
interfering with Action View’s detection of nested attributes
acceptance, but I’m not sure exactly why.

Chris

From 0829776dd45b3a0c57134f7af5d4f21feebe5fd7 Mon Sep 17 00:00:00 2001
From: Chris M. [email protected]
Date: Tue, 3 May 2011 18:11:40 +0100
Subject: [PATCH] Fix stubbing for nested form in assets/new.


spec/views/assets/new.html.erb_spec.rb | 11 +++++++±–
1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/spec/views/assets/new.html.erb_spec.rb
b/spec/views/assets/new.html.erb_spec.rb
index b8845fe…9bf3b26 100644
— a/spec/views/assets/new.html.erb_spec.rb
+++ b/spec/views/assets/new.html.erb_spec.rb
@@ -1,13 +1,18 @@
require ‘spec_helper’

describe “assets/new.html.erb” do

  • let(:asset) { mock_model(“Asset”).as_new_record.as_null_object }
  • let(:owner) { mock_model(“Owner”).as_new_record.as_null_object }
  • let(:asset) { mock_model(“Asset”).as_new_record }

  • let(:owner) { mock_model(“Owner”).as_new_record }

    before(:each) do
    asset.stub(:owner => owner)

  • asset.accepts_nested_attributes_for :owner
  • asset.stub(:name)

  • asset.stub(:owner_attributes=)

  • owner.stub(:name)

  • assign(:asset, asset)

  • end

    it “renders new asset form with owner attributes” do

    1.7.4.4