2014-01-22 2 views
2

Я хочу проверить включение модуля в класс. Я пытаюсь определить новый класс в RSpec:Определить класс в Rspec

describe Statusable do 
    let(:test_class) do 
    class ModelIncludingStatusable < ActiveRecord::Base 
     include Statusable 
     statuses published: "опубликовано", draft: "черновик" 
    end 
    end 

    describe '#statuses' do 
    it 'sets STATUSES for a model' do 
     test_class::STATUSES.should == ["опубликовано", "черновик"] 
    end 
    end 
end 

И я получаю ошибку:

TypeError: 
     [ActiveModel::Validations::InclusionValidator] is not a class/module 

Это, вероятно, потому, что в Statusable у меня есть:

validates_inclusion_of :status, :in => statuses, 
      :message => "{{value}} должен быть одним из: #{statuses.join ','}" 

Но если я комментирую это, я получаю:

TypeError: 
     ["опубликовано", "черновик"] is not a class/module 

Возможно, новое определение класса - не самый лучший вариант, что мне делать тогда? И даже если это не так, как я могу определить класс в RSpec? И как я могу исправить эту ошибку?

ответ

14

Не определяйте новую константу в тестах, иначе она будет загрязнять другие тесты. Вместо этого используйте stub_const.

Кроме того, для этого является модульным тестом модуля Statusable. Если модель ActiveRecord не является необходимостью, лучше не использовать ее.

Вы также можете использовать class_eval, чтобы избежать не открывать этот класс (независимо от того, подделка или нет) на самом деле

describe Statusable do 
    before do 
    stub_const 'Foo', Class.new 
    Foo.class_eval{ include Statusable } 
    Foo.class_eval{ statuses published: "foo", draft: "bar"} 
    end 

    context '#statuses' do 
    it 'sets STATUSES for a model' do 
     FOO::STATUSES.should == ["foo", "bar"] 
    end 
    end 
end 

Хотя я скопировал ваше утверждение, я хотел бы предложить, чтобы не вставить константу сказать STATUS в классе/модуль (Foo), который включает этот модуль. Вместо этого классный метод был бы лучше

expect(Foo.status).to eq(["foo", "bar"]) 
+0

Спасибо, это работает. Почему метод класса предпочтительнее? – leemour

+1

@leemour, мое удовольствие. Две причины для метода класса: 1. имя константы должно быть статическим.В вопросе «STATUS» фактически находится под пространством имен «Foo» для чтения как «Statusable :: STATUS», но теперь это «FOO :: STATUS», который намеревается означать одно и то же. 2. Постоянное значение должно быть статическим, вы не можете ожидать, что значение константы изменится в соответствии с кодом. –

+0

Хорошо, но я определял статус в статусе '# status ', когда он вызывался в классе, который включал' Statusable', как в моем коде выше. Плохое решение? – leemour

0

Когда вы вызываете let, определите метод memoized helper. Вы не можете определить значение class.

0

Другой вариант, который я часто использую, состоит в том, чтобы провести весь тест в своем собственном модуле, например.

module Mapping::ModelSpec 
    module Human 
     Person = Struct.new(:name, :age, :posessions) 
     Possession = Struct.new(:name, :value) 
    end 

    RSpec.describe Mapping::Model do 
     it 'can map with base class' do 
      person = Human::Person.new('Bob Jones', 200, []) 

      ... 
     end 
    end 
end 

Хотя это немного громоздким, он избегает загрязнять глобальное пространство имен, только немного более синтаксис, и, как правило, легко понять. Лично я хотел бы получить лучший вариант. Но я не уверен, что это будет.

2

Это не получается, потому что определение класса не возвращает себя.

$ irb 
> class Foo; 1 end 
=> 1 

вам нужно сделать так:

let(:test_class) do 
    class ModelIncludingStatusable < ActiveRecord::Base 
     include Statusable 
     statuses published: "опубликовано", draft: "черновик" 
    end 
    ModelIncludingStatusable # return the class 
    end 

Он работает, но, к сожалению, ModelIncludingStatusable будет определяться на высшем уровне, потому что правила рубина.

Чтобы ваш класс заключать в капсулу, вы должны сделать так:

class self::ModelIncludingStatusable < ActiveRecord::Base 
    include Statusable 
    statuses published: "опубликовано", draft: "черновик" 
    end 
    let(:test_class) do 
    self.class::ModelIncludingStatusable # return the class 
    end 

Он отлично работает :)

Смежные вопросы