关于ruby on rails:如何在不关联数据库表的情况下RSpec共享ActiveRecord模块?

How to RSpec a shared ActiveRecord module without associated database table?

使用RSpec 2.6 / Rails 3.1 / Postgres:

我正在编写一个支持模块(在我的lib /中),任何AR模型都可以包含该模块。我想为此模块编写规格。它需要包含在AR :: Base模型中,因为当包含它时它会加载关联,并且依赖于某些AR方法,但是在为该模块编写rspec时我不想使用我现有的模型。

我只想创建一个任意的AR模型,但是很显然,它在数据库中没有关联的表,而AR正在消失。这是我想做的事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
class SomeRandomModel < ActiveRecord::Base
  include MyModule

  # simulate DB attributes that MyModule would be using
  attr_accessor :foo, :bar, :baz
end

describe SomeRandomModel do
  it '#some_method_in_my_module' do
    srm = SomeRandomModel.new(:foo => 1)
    srm.some_method_in_my_module.should eq(something)
  end
end

当然,我在postgres中遇到了关于该关系不存在的错误。

感谢您的帮助!


有一种使用rpsecs shared_examples_for来解决此问题的替代方法,我在代码片段中提到了一些技巧,但有关更多信息,请参见此relishapp-rspec-guide。

使用此方法,您可以在包含该模块的任何类中测试您的模块。因此,您实际上正在测试您在应用程序中使用的功能。

让我们看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Lets assume a Movable module
module Movable
  def self.movable_class?
    true
  end

  def has_feets?
    true
  end
end

# Include Movable into Person and Animal
class Person < ActiveRecord::Base
  include Movable
end

class Animal < ActiveRecord::Base
  include Movable
end

现在让我们为我们的模块创建规格:movable_spec.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
shared_examples_for Movable do
  context 'with an instance' do
    before(:each) do
      # described_class points on the class, if you need an instance of it:
      @obj = described_class.new

      # or you can use a parameter see below Animal test
      @obj = obj if obj.present?
    end

    it 'should have feets' do
      @obj.has_feets?.should be_true
    end
  end

  context 'class methods' do
    it 'should be a movable class' do
      described_class.movable_class?.should be_true
    end
  end
end

# Now list every model in your app to test them properly

describe Person do
  it_behaves_like Movable
end

describe Animal do
  it_behaves_like Movable do
    let(:obj) { Animal.new({ :name => 'capybara' }) }
  end
end


我遇到了类似的问题,经过大量的搜索之后,我决定只在RSpec测试中设置和删除表。这是我一直在使用的代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
describe"stuff you are testing do" do

  before :all do
    m = ActiveRecord::Migration.new
    m.verbose = false  
    m.create_table :random_class do |t|
      t.string :field_1
      t.integer :field_2
    end
  end

  after :all do
    m = ActiveRecord::Migration.new
    m.verbose = false
    m.drop_table :random_class
  end

  class RandomClass < ActiveRecord::Base
    attr_accessible :field_1, :field_2
  end

  # Your regular RSpec before(:each) blocks and tests
  # ...

  # e.g.
  it"should be able to use RandomClass" do
    rc = RandomClass.create! :field_1 =>"hello", :field_2 => 5
    rc.field_1.should =="hello"
    rc.field_2.should == 5
  end

end

我不喜欢这种解决方案,但是它有效。希望这对某人有帮助!要么激励他们发布实现此目标的最佳方法。

:)


您可以覆盖self.columns

1
2
3
4
5
class Tableless < ActiveRecord::Base
  def self.columns
    @columns ||= [];
  end
end

然后您可以创建一个新的实例法规

1
2
let(:dummy_instance) { Tableless.new }
it { is_expected.to be_valid}


这与goggin13的答案略有不同,它可以解决迁移问题(如Zac所述)并修复表名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
describe"stuff you are testing do" do

  before :all do
    m = ActiveRecord::Migration
    m.verbose = false  
    m.create_table :random_classes do |t|
      t.string :field_1
      t.integer :field_2
    end
  end

  after :all do
    m = ActiveRecord::Migration
    m.verbose = false
    m.drop_table :random_classes
  end

  class RandomClass < ActiveRecord::Base
    attr_accessible :field_1, :field_2
  end

  # Your regular RSpec before(:each) blocks and tests
  # ...

  # e.g.
  it"should be able to use RandomClass" do
    rc = RandomClass.create! :field_1 =>"hello", :field_2 => 5
    rc.field_1.should =="hello"
    rc.field_2.should == 5
  end

end


goggin13,谢谢您的回答。它对我最有用,除了我必须将迁移实例方法更改为类方法。即:

来自

1
2
3
 m = ActiveRecord::Migration.new
 m.verbose = false  
 m.create_table ...

1
2
3
 m = ActiveRecord::Migration
 m.verbose = false  
 m.create_table ...