# How do I write an RSpec test to unit-test this interesting metaprogramming code?

**URL:** <https://www.ruby-forum.com/t/how-do-i-write-an-rspec-test-to-unit-test-this-interesting-metaprogramming-code/189061>\
**Category:** Ruby\
**Created:** [May 23, 2010, 4:00pm UTC](https://www.ruby-forum.com/t/how-do-i-write-an-rspec-test-to-unit-test-this-interesting-metaprogramming-code/189061 "2010-05-23T16:00:42Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![aftershock](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/8e7dd6/32.png) [@aftershock](https://www.ruby-forum.com/u/aftershock)\
**Post date:** [May 23, 2010, 4:00pm UTC](https://www.ruby-forum.com/t/how-do-i-write-an-rspec-test-to-unit-test-this-interesting-metaprogramming-code/189061/1 "2010-05-23T16:00:42Z")

</div>

I’m a little stumped by this problem. Here’s some simple code that,  
for each argument specified, will add specific get/set methods named  
after that argument. If you write `attr_option :foo, :bar`, then you  
will see `#foo/foo=` and `#bar/bar=` instance methods on `Config`:

```
module Configurator
  class Config
    def initialize()
      @options = {}
    end

    def self.attr_option(*args)
      args.each do |a|
        if not self.method_defined?(a)
          define_method "#{a}" do
            @options[:"#{a}"] ||= {}
          end

          define_method "#{a}=" do |v|
            @options[:"#{a}"] = v
          end
        else
          throw Exception.new("already have attr_option for #{a}")
        end
      end
    end
  end
end

```

So far, so good. I want to write some RSpec tests to verify this code  
is actually doing what it’s supposed to. But there’s a problem! If I  
invoke `attr_option :foo` in one of the test methods, that method is  
now forever defined in Config. So a subsequent test will fail when it  
shouldn’t, because `foo` is already defined:

```
  it "should support a specified option" do
    c = Configurator::Config
    c.attr_option :foo
    # ...
  end

  it "should support multiple options" do
    c = Configurator::Config
    c.attr_option :foo, :bar, :baz # Error! :foo already defined
                                     # by a previous test.
    # ...
  end

```

Is there a way I can give each test an anonymous “clone” of the  
`Config` class which is independent of the others?

---

<div class="post-metadata">

**Author:** ![aftershock](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/8e7dd6/32.png) [@aftershock](https://www.ruby-forum.com/u/aftershock)\
**Post date:** [May 24, 2010, 4:35am UTC](https://www.ruby-forum.com/t/how-do-i-write-an-rspec-test-to-unit-test-this-interesting-metaprogramming-code/189061/2 "2010-05-24T04:35:47Z")

</div>

On 5/23/10, James W. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> else
> 
> ```
> 
> now forever defined in Config. So a subsequent test will fail when it  
> c.attr\_option :foo, :bar, :baz # Error! :foo already defined  
> # by a previous test.  
> # …  
> end
> 
> Is there a way I can give each test an anonymous “clone” of the  
> `Config` class which is independent of the others?

As I see it, you have several options:

1. remove the exception you are raising at the end of attr\_option.
2. intercept and ignore that exception when you call attr\_option in your  
tests.
3. remove the methods you added at the end of each test. Something  
like this should work:  
c.send :undef\_method, :foo
4. (what you were asking about) make a copy of Configurator::Config  
before changing it. This should work:  
c=Configurator::Config.clone

PS: c is an especially unlucky choice for a local variable name, I  
have found. If you ever have to run your program under one of the  
console-mode debuggers (I do this all the time) it will get confused  
with the continue command, which is abbreviated c, often with highly  
frustrating results.

---

<div class="post-metadata">

**Author:** ![aftershock](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/8e7dd6/32.png) [@aftershock](https://www.ruby-forum.com/u/aftershock)\
**Post date:** [May 24, 2010, 1:13pm UTC](https://www.ruby-forum.com/t/how-do-i-write-an-rspec-test-to-unit-test-this-interesting-metaprogramming-code/189061/3 "2010-05-24T13:13:45Z")

</div>

On Sun, May 23, 2010 at 4:00 PM, James W. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> else
> 
> ```
> 
> now forever defined in Config. So a subsequent test will fail when it  
> c.attr\_option :foo, :bar, :baz # Error! :foo already defined  
> # by a previous test.  
> # …  
> end  
> Caleb is right, c is not a good name ;), however

lambda{ c.attr\_option… }.should raise\_error( WhatWasIt)

I call it WhatWasIt because you really should define your own Exception,  
e.g.  
IllegalMonitorState = Class::new RuntimeError

please take care to subclass RuntimeError, subclassing Exception is  
waaaay toooo general.

HTH  
R.

---

<div class="post-metadata">

**Author:** ![aftershock](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/8e7dd6/32.png) [@aftershock](https://www.ruby-forum.com/u/aftershock)\
**Post date:** [May 26, 2010, 11:49pm UTC](https://www.ruby-forum.com/t/how-do-i-write-an-rspec-test-to-unit-test-this-interesting-metaprogramming-code/189061/4 "2010-05-26T23:49:42Z")

</div>

On May 23, 2010, at 07:00 , James W. wrote:

> ```
> end
> 
> it "should support multiple options" do
> c = Configurator::Config
> c.attr_option :foo, :bar, :baz # Error! :foo already defined
> # by a previous test.
> # ...
> end
> 
> ```

Caleb and Robert are nit-picking… ‘c’ is a perfectly fine name for a  
variable in a 4 line test/spec.

The problem you’re having is easily solved by using anonymous  
subclasses:

```
 it "should support a specified option" do
   c = Class.new(Configurator::Config)
   c.attr_option :foo
   # ...
 end

```

That makes a throwaway class that has all the same features of the  
superclass without any of the infectious properties of calling your attr  
methods on the real thing.
