On Nov 24, 2011, at 12:48 AM, Anran Yang wrote:

> Dear all,
> 
> I recently tried to write a custom matcher using rspec dsl, which I'd like to 
> use as the following:
> 
> ==========hpgc_spec.rb===================
> 
> require "spec_helper"
> 
> describe Hpgc do
>   specify do 
>     should be_structured_as(
>     { :app => {
>         :has => [
>             :name, 
>             :author, 
>             :org, 
>             :version, 
>             :abstract, 
>             :uri 
>         ],
> 
>         :can_be_a => { 
>             :dataset => { 
>                 :can_be_a => {
>                     :vector => {}, 
>                     :raster => {}
>                 }
>             },
> 
>             :program => { 
>                 :has => [
>                     :language, 
>                     :parallel_programming_model, 
>                     :parallel_performance_overview, 
>                     :category 
>                 ]
>             }
>         },
> 
>         :has_many => {
>             :app_option => {
>                 :has => [
>                     :switch, 
>                     :description 
>                 ]
>             },
> 
>             :app_io => { 
>                 :has => [
>                     :name, 
>                     :description, 
>                     :is_in 
>                 ]
>             }
>         }
>     }})
>   end
> end
> 
> ===========================================
> 
> The purpose is to test the entire model structure(attributes, inheritance and 
> association).

Then term Behavior Driven Development came into being, in part, to discourage 
this approach to testing. We believe testing should be about behavior, and that 
testing structures leads to highly coupled tests that are hard to change.

I'd recommend thinking about the need for each of these attributes, come up 
with examples of how they are used and write those instead. That said, see 
below for more on how matchers work.

> Then I wrote following code to implement it:
> 
> ========be_structured_as.rb===================
> 
> # This is a matcher for test hpgc model structure
> 
> RSpec::Matchers.define :be_structured_as do |structure|
>   def check(namespace, klass, desc)
>     (desc[:has_many] ||= {}).keys.each { |item| 
> klass.to_s.camelize.constantize.new.should have_many 
> item.to_s.pluralize.to_sym }
>     if !desc[:can_be_a] || desc[:can_be_a].empty?
>       obj = Factory.create(klass)
>       obj.should have_these_attributes (desc[:has] ||= [])
>     else
>       desc[:can_be_a].keys.map { |sub| sub.to_s }.should == 
> namespace.subclasses_of(klass.to_s)
>       desc[:can_be_a].each { |key, value| check namespace, key, value }
>     end
>   end
> 
>   match do |namespace|
>     self.class.send :include, namespace
>     structure.each { |key, value| check namespace, key, value }
>   end
> end
> 
> ===============================================
> 
> To simplify the matcher definition,  I used some other machers like 
> "obj.should have_these_attributes (desc[:has] ||= [])", this works fine, 
> thanks to the excellent new matcher dsl syntax. Howerver, I got trouble when 
> running the test using "rspec spec/models/hpgc_spec.rb -fd". The test passed 
> but print
> 
> <Screenshot.png>
> 
> instead of something like "Hpgc should be structured as ...". I think it may 
> due to the other matcher I used because the message is from the matcher 
> "have_these_attributes".

That's correct. The default behavior is that the block passed to the "match" 
method in the matcher DSL is expected to return a boolean. If it raises an 
exception instead, that exception bubbles up and becomes the source of the 
failure message.

> I try to add method "description" but seems it doesn't work.

"description" is used for the documentation formatter, not for failure 
messages. If you run "rspec spec --format documentation" you'll see names for 
all your examples.

> And the failure message is also a problem. I search the web and come across 
> some solutions, but all of them need manually build the message. I've not 
> come out a clean way to do this(I must add some invasive clauses to track the 
> error) and think maybe the best behaviour would be that it "throw" the 
> failure message of inner matchers or false-value expressions. But this time 
> it just told me something like "Hpgc is expected to be structured as ...".

Instead of using the "match" method in the matcher, you can use 
"match_unless_raises", which returns false if it captures an exception, letting 
the matcher control the failure message (which is what I think you're looking 
for):

RSpec::Matchers.define :be_structured_as do |structure|
  def check(namespace, klass, desc)
    # ....
  end

  match_unless_raises do |namespace|
    self.class.send :include, namespace
    structure.each { |key, value| check namespace, key, value }
  end
end

HTH,
David

> Any idea?
> 
> Best Regards,
> 
> YANG Anran


_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to