rspec에서 NameError: wrong constant name 오류 해결

조회수 1432회

polymorphic association에서 factory_gir을 사용하여 rspec 상에서 unit test를 작성하는데 어려움이 있습니다.

실험을 위해서 아주 기본 예제로 하였는데

기본예제에서도 왜인지 파악되지 않는 오류가 있어서 이렇게 도움을 청합니다...

# db/schema.rb

ActiveRecord::Schema.define(version: 20160412134106) do

  create_table "cars", force: :cascade do |t|
    t.string   "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "pictures", force: :cascade do |t|
    t.integer  "imageable_id"
    t.string   "imageable_type"
    t.string   "name"
    t.datetime "created_at",     null: false
    t.datetime "updated_at",     null: false
  end

end

schema가 위와 같이 되어있고,

아주 기본적인 polymorphic association을 하였습니다.

# app/models/picture.rb

class Picture < ActiveRecord::Base
  belongs_to :imageable, polymorphic: true
end
# app/models/car.rb

class Car < ActiveRecord::Base
  has_many :pictures, as: :imageable
end

위와 같이 rails polymorphic 예제와 동일하게 구성해보았습니다.

그리고 factory girl과 rspec를 이용해서 test를 아래와 같이 작성하였는데요...

# spec/factories/pictures.rb

FactoryGirl.define do
  factory :car_picture do
    association :imageable, factory: :car
  end
end
# spec/factories/cars.rb

FactoryGirl.define do
  factory :car do
    name "MyString"
  end
end
# spec/models/car_spec.rb

require 'rails_helper'

RSpec.describe Car, type: :model do
  let(:car_picture) { build(:car_pictur) }
  it { expect(car_picture).to validate_uniqueness_of(:imageable_type).scoped_to(:imageable_id) }
end

rspec 명령을 수행하면

$ bundle exec rspec -b
F

Failures:

  1) Car
     Failure/Error: let(:car_picture) { build(:car_picture) }

     NameError:
       uninitialized constant CarPicture

     ...

Finished in 0.00241 seconds (files took 1.4 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/models/car_spec.rb:5 # Car

$

위와 같이 uninitialized constant CarPicture라는 오류가 나는데 왜 나는지 모르겠네요...

추가 질문

댓글에서 언급한 오류를 질문용 프로젝트에도 재현하였습니다.

# app/models/picture.rb

class Picture < ActiveRecord::Base
  belongs_to :imageable, polymorphic: true

  validates :imageable_type, uniqueness: { scope: :imageable_id } # 추가된 행!!!!!!!
  validates :imageable, presence: true # 추가된 행!!!!!!!
end

다른 코드들은 답변으로 제시해주신 factory명과 해당 클래스명이 다를때 사용하는 class: Picture를 추가하였고, 나머지는 전부 같고 위 picture.rb에서 마지막쯤에 validates :imageable, presence: true # 추가된 행!!!!!!!을 추가하고 test를 돌리니

아래와 같은 오류가 발생합니다.(댓글에서도 언급한..)

$ bundle exec rspec
F

Failures:

  1) Car should validate that :imageable_type is case-sensitively unique within the scope of :imageable_id
     Failure/Error: it { expect(car_picture).to validate_uniqueness_of(:imageable_type).scoped_to(:imageable_id) }

     NameError:
       wrong constant name cAR
     # ./spec/models/car_spec.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.03813 seconds (files took 1.39 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/models/car_spec.rb:5 # Car should validate that :imageable_type is case-sensitively unique within the scope of :imageable_id

$

spec/factories/pictures.rb에서

FactoryGirl.define do
  factory :car_picture, class: Picture do
    association :imageable, factory: :user
  end
end

car대신 user로 바꾸니

$ bundle exec rspec
F

Failures:

  1) Car should validate that :imageable_type is case-sensitively unique within the scope of :imageable_id
     Failure/Error: it { expect(car_picture).to validate_uniqueness_of(:imageable_type).scoped_to(:imageable_id) }

     NameError:
       wrong constant name uSER
     # ./spec/models/car_spec.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.04852 seconds (files took 2.14 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/models/car_spec.rb:5 # Car should validate that :imageable_type is case-sensitively unique within the scope of :imageable_id

$

위와 같이 wrong constant name uSER과 같이 모델이름에서 첫문자만 소문자이고 나머지는 전부 대문자로 되는현상이 보였고, 모델이름이 UserFriend라면 uSERfRIEND 로 오류가 나느 현상이 보여졌습니다...

뭐가 문제일까요?

  • (•́ ✖ •̀)
    알 수 없는 사용자

2 답변

  • Problem:

    # spec/factories/pictures.rb
    
    FactoryGirl.define do
      factory :car_picture do
        association :imageable, factory: :car
      end
    end
    

    spec/factories/pictures.rb에 정의하신 factory :car_picture 이 부분이 문제가 되는 부분인데요.

    Solution:

    Factory명이 해당 클래스명과 다를시엔,

    FactoryGirl.define do
      factory :car_picture, class: Picture
        ...
      end
    end
    

    이렇게 정의 해주셔야합니다.

    만약, 상속 개념을 사용하신다면

    FactoryGirl.define do
      factory :picture do
        ... # 공통적으로 사용할 attributes은 여기로
      end
    
      factory :car_picture, parent: :picture do
        association :imageable, factory: :car
      end
    end
    

    이런식으로 사용하시면 됩니다.

    • (•́ ✖ •̀)
      알 수 없는 사용자
    • (•́ ✖ •̀)
      알 수 없는 사용자
    • 답변감사합니다. 예제코드에서는 말씀해주신대로 하니까 잘 됐는데... 실제 프로젝트 코드에서는 또 요상하게 오류가 나네요... `wrong constant name uSER` 이런식...? 알 수 없는 사용자 2016.4.13 02:02
    • 저 user이 `association :imageable, factory: :car` 이부분에서 car대신 :user을 넣었더니 저렇게 되고 저 자리에 또 다른 (실제 있는) factory를 넣으니까 같은 형태로 첫문자만 소문자 뒷 문자는 전부 대문자로 오류가 나네요... car을 넣으면 cAR, card를 넣으면 cARD... 이건 뭘까요...? 알 수 없는 사용자 2016.4.13 02:05
    • stacktrace는 거의 비슷하네요...? 똑같나... 아무튼 얼핏 보기에 똑같습니다... 알 수 없는 사용자 2016.4.13 02:06
    • 새로 발생하는 문제에 대해선 질문을 새로 작성해서 작성하신 코드와 함께 올려주세요. 코드 없이 설명만으론 문제점을 찾기가 힘드네요. 알 수 없는 사용자 2016.4.13 02:52
    • @김현일님 계속적인 답변 감사드립니다. 윗 댓글에서 언급한 새로 발생한 문제에 대해서 본문에 추가하였습니다. 한번서 살펴봐 주셔요... 감사합니다! 알 수 없는 사용자 2016.4.13 13:49
  • 추가 질문에 대한 답변

    Problem:

    NameError:
      wrong constant name cAR
    

    shoulda-matchersvalidate_uniqueness_of 메소드는 uniqueness를 테스트하기 위해서 내부적으로 두개의 객체를 생성합니다. 한개는 uniqueness로 정의된 attribute과 같은 값, 또 하나는 uniqueness로 정의된 attribute과 다른 값을 가진 객체 두개를 생성합니다.

    지금 uniqueness를 테스트하려는 attribute이, polymorphic association의 :imageable_type인데,

    uniqueness를 테스트하기 위해 두개의 객체를 생성하는 과정에서

    "Car" 클래스명을 가진 객체를 하나 생성하고, "cAR" 클래스명을 가진 객체를 하나 생성하려고 하는데, cAR라는 클래스가 존재하지 않기 때문에 에러가 발생하는 것입니다.

    • 여기서 "cAR" 클래스명이 어떻게 나왔냐면, shoulda-matchers 내부적으로 다른 값을 생성할 때, value.swapcase와, value.next 이 두가지 메소드를 사용하여 다른 값을 생성합니다. 여기서 "Car".swapcase의 결과 값이 "cAR"이 됩니다. (참고: "User".swapcase의 결과 값은 "uSER").

    Solution:

    해결 방법은,

    class Picture < ActiveRecord::Base
      ...
      validates :imageable_id, uniqueness: { scope: :imageable_type }
    end
    
    RSpec.describe Car, type: :model do
      ...
      it { expect(car_picture).to validate_uniqueness_of(:imageable_id).scoped_to(:imageable_type) }
    end
    

    테스트하려는 uniqueness의 attribute를 imageable_id으로 하고, imageable_type을 scope으로 정의하면 원하시는 테스트를 하실 수 있을거같습니다.

    • (•́ ✖ •̀)
      알 수 없는 사용자
    • (•́ ✖ •̀)
      알 수 없는 사용자

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)