create tests that extend rspec classes instead of using describe #427

Closed
brianmario opened this Issue Jul 15, 2011 · 17 comments

Projects

None yet

6 participants

@brianmario

This may already be possible, but I haven't been able to get it working correctly.

I tried following https://gist.github.com/921590 with some slight variation - instead of clobbering Test::Unit::TestCase I just tried to extend RSpec::Core::ExampleGroup with my test class, like you normally do with Test::Unit.

Ideally I'd like to have my own classes for units, functionals and integration tests that would extend/mixin what they need to from RSpec classes and modules to allow the exact same behavior as using describe.

Since I'm terrible at explaining things, here's what I'm trying to do in code:

class SomeIntegrationTest < SomeRspec::Request::BaseClass
  before :all do
    # ...
  end

  it "tests something" do
    # ...
  end

  context "some context" do
    before :each do
      # ...
    end

    it "tests something else" do
      # ...
    end
  end
end

Basically, to allow me to extend RSpec classes instead of using describe with no changes to the test code inside as if I were still using describe.

This would allow me to setup my own versions of the various RSpec base classes where I'd mixin custom functionality without the need to reopen the RSpec classes themselves.

As I said, I'm not actually sure if this is already possible but I'd be a huge help in my mission to migrate more tests away from Test::Unit to rspec2.

@dchelimsky
Member

RSpec supports extending example groups with modules like this:

RSpec.configure do |config|
  config.include SomeModule
  config.extend OtherModule
end

You can always do same right in the spec files, rather than globally:

describe "something" do
  include SomeModule
  # etc ...
end

Or you can constrain it from the config file (e.g. https://github.com/rspec/rspec-rails/blob/master/lib/rspec/rails/example.rb#L15-17).

Would that satisfy your need?

@dchelimsky
Member

The subject of this, I see, is "instead of using describe" - right now that is not supported.

@brianmario

ah ok thanks, if I can muster up a patch do you think you'd be interested?

I wouldn't want any existing behavior to change at all, just add the
ability to extend classes (I don't mind needing to mix in modules like
RequestGroup or whatever from there).

On Jul 16, 2011, at 10:55 AM, dchelimsky
reply@reply.github.com
wrote:

The subject of this, I see, is "instead of using describe" - right now that is not supported.

Reply to this email directly or view it on GitHub:
#427 (comment)

@dchelimsky
Member

Are you unable to get what you need from the solutions that already exist?

@brianmario

I'd like to create my own classes for what are currently implemented as ModelExample, ControllerExample and RequestExample modules. I have some custom functionality that I'd like to include into said classes independently. Right now the only way (that I know of) to do this given existing functionality in RSpec is to include the functionality in every example group like your example above:

RSpec.configure do |config|
  config.include SomeModule
  config.extend OtherModule
end

Or include it manually in every single describe call like your other example:

describe "something" do
  include SomeModule
  # etc ...
end

Ideally I could do this once in each class type (Model, Controller or Request) depending on which need what functionality, and just extend that class for all of my tests of that type.

@justinko
Contributor

How big is this test suite you got? Since RSpec supports t/u assertions, why not just go "all the way"? If this is at Github, make all the developers convert the suite for an hour. And make sure that keg is full :)

@brianmario

I'm already planning on using the t/u assertions, and aliasing it to test just to minimize the amount of change. I really need the ability to use classes instead of the regular DSL though. Oh and the test suite is massive :P

@justinko
Contributor

Is Github on 1.9.2? If so, you could just use MiniTest and get an RSpec like setup:

http://metaskills.net/2011/03/26/using-minitest-spec-with-rails/

@brianmario

1.8.7 actually

@justinko
Contributor

Alright let me dig in.

@brianmario

What I'm aiming for is something like:

class MyRequestClass < Rspec::Core::ExampleGroup
  include Rspec::Rails::RequestExampleGroup
  include MyCustomFunctionality

  before(:all) do
    # some custom setup stuff for every instance of this class
  end
end

class MyTestClass < MyRequestClass
  before(:all) do
    # some more setup, specific to this test case
    # ideally this should also run the before(:all) in the base class too right?
    @rspec2 = "awesome"
  end

  test "should be awesome"
    assert_equal "awesome", @rspec2
  end
end

Doesn't need to be exactly like that, but hopefully you get the idea.

@myronmarston
Member

Right now the only way (that I know of) to do this given existing functionality in RSpec is to include the functionality in every example group like your example above...or include it manually in every single describe call like your other example

There's a 3rd option: use RSpec metadata to configure what modules get included in which example groups:

RSpec.configure do |c|
  c.treat_symbols_as_metadata_keys_with_true_values = true
  c.include ModelSpecHelpers, :model
end

describe User, :model do
end

You can even make this less verbose by establishing conventions based on the directory structure:

RSpec.configure do |config|
  config.include ModelSpecHelpers, :example_group => { :file_path => %r{spec/models} }
end

Any spec defined under spec/models will have this module included automatically.

Overall, I'm not against adding support for classes as you've requested, but I would question the need for it. In my experience, modules are provide superior flexibility to classes for inheritance/sharing method implementations, and unless you specifically need to instantiate an object--modules are usually better. RSpec already has lots of flexible ways to use modules in your example groups.

Can you give an example of something that would be possible with classes but not with the module approach RSpec already provides?

@myronmarston
Member

Sorry, I think we were typing replies at the same time. Here's how I would re-work the example you posted with what RSpec already provides:

RSpec.configure do |c|
  c.treat_symbols_as_metadata_keys_with_true_values = true
end

shared_context :request_examples, :request do
  include Rspec::Rails::RequestExampleGroup
  include MyCustomFunctionality

  before(:all) do
    # some custom setup stuff for every instance of this class
  end
end

describe MyTest, :request do
  before(:all) do
    # some more setup, specific to this test case
    # ideally this should also run the before(:all) in the base class too right?
    @rspec2 = "awesome"
  end

  test "should be awesome"
    assert_equal "awesome", @rspec2
  end
end

Or you could use the file path stuff I mentioned above if you prefer that to being explicit with your metadata.

@brianmario

Interesting... I'll take a look at something along these lines.

The desire for using classes is more of a legacy/migration concern than anything else. I much prefer using modules as well, but doing so on this codebase may require a bit of refactoring where I'm hoping to keep changes to a minimum.

For other users wanting to migrate from Test::Unit to rspec, being able to just replace the base-class they're inheriting from and have things (for the most part) JustWork would be amazing. I understand the changes required to do so might not align with the goals of the RSpec(-core) project. But perhaps a gem that would ease the transition could be useful?

@dchelimsky
Member

RSpec-1 had an easy way to transition, but it required some architectural hax that didn't survive the rspec-core rewrite.

There is a gem that aims to ease the transition, but from what I can tell it doesn't really work for anything but un-extended t/u: https://github.com/glv/rspec-unit.

@charliemaffitt
Contributor

This issue hasn't had any action in a year; with the suggested alternative from @myronmarston appearing to be viable I suggest it be closed.

@alindeman
Contributor

I agree. Ping us to reopen if you disagree.

@alindeman alindeman closed this Dec 6, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment