2013-03-28 2 views
0
와 테스트 다중 사용자 검사기가 나는 두 개의 사용자 정의 유효성 검사기에 대한 사양을 실행하려고

:이 RSpec에

spec/validators/email_validator_spec.rb 
spec/validators/phone_validator_spec.rb 

내가 실행 bundle exec rspec spec/validators/phone_validator_spec.rb 사양은 실패

1) PhoneValidator with a valid phone number should be valid 
    Failure/Error: subject.should be_valid 
     expected valid? to return true, got false 
    # ./spec/validators/phone_validator_spec.rb:20:in `block (4 levels) in <top (required)>' 
    # ./spec/validators/phone_validator_spec.rb:18:in `each' 
    # ./spec/validators/phone_validator_spec.rb:18:in `block (3 levels) in <top (required)>' 

을하지만, 내가 그 사양을 실행할 때 개별적으로 bundle exec rspec spec/validators/phone_validator_spec.rb 명령을 사용하면 통과합니다.

email_validator_spec.rb을 제거하면 phone_validator_spec.rb 번을 사용하여 다음 명령을 사용하여 전달합니다. bundle exec rspec spec/validators/.

bundle exec rspec spec/validators/을 실행할 때 두 사양이 모두 통과 될 것으로 예상됩니다. 아무도 나에게 무슨 일이 일어 났는지 설명 할 수 있니?

업데이트 : 중고 zetetic의 끝이 오류 해시를 인쇄하려면 :

1) PhoneValidator with a valid phone number should be valid 
    Failure/Error: subject.errors.should == {} 
     expected: {} 
      got: #<ActiveModel::Errors:0x37b2460 @base=#<Validatable:0x37b2700 @validation_context=nil, @errors=#<ActiveModel::Errors:0x37b2460 ...>, @phone_number="1112223333">, @messages={:email=>["is invalid"]}> (using ==) 
     Diff: 
     @@ -1 +1,8 @@ 
     +#<ActiveModel::Errors:0x37b2460 
     + @base= 
     + #<Validatable:0x37b2700 
     + @errors=#<ActiveModel::Errors:0x37b2460 ...>, 
     + @phone_number="1112223333", 
     + @validation_context=nil>, 
     + @messages={:email=>["is invalid"]}> 
    # ./spec/validators/phone_validator_spec.rb:21:in `block (4 levels) in <top (required)>' 
    # ./spec/validators/phone_validator_spec.rb:18:in `each' 
    # ./spec/validators/phone_validator_spec.rb:18:in `block (3 levels) in <top (required)>' 

모두 사양이 실행될 때 Validatable 클래스 정의가 결합되어 나타납니다. 이 행동이 예상됩니까? 고유 한 클래스 이름을 사용하면 두 스펙이 모두 전달됩니다.

사양/검증/phone_validator_spec.rb

require 'active_model' 
require 'rspec/rails/extensions' 
require File.expand_path('app/validators/phone_validator') 

class Validatable 
    include ActiveModel::Validations 
    attr_accessor :phone_number 
    validates :phone_number, phone: true 
end 

describe PhoneValidator do 

    subject { Validatable.new } 

    describe "with a valid phone number" do 
    it "should be valid" do 
     phone_numbers = ["1112223333", "123222ABCD"] 
     phone_numbers.each do |phone_number| 
     subject.phone_number = phone_number 
     subject.should be_valid 
     end 
    end 
    end 
end 

응용 프로그램/검증/phone_validator.rb

class PhoneValidator < ActiveModel::EachValidator 
    def validate_each(object, attribute, value) 
    return if value.blank? 
    unless value =~ /^[A-Za-z0-9]{10}$/ 
     object.errors[attribute] << (options[:message] || "is not formatted properly") 
    end 
    end 
end 

사양/검증/email_validator_spec.rb

require 'active_model' 
require 'rspec/rails/extensions' 
require File.expand_path('app/validators/email_validator') 

class Validatable 
    include ActiveModel::Validations 
    attr_accessor :email 
    validates :email, email: true 
end 

describe EmailValidator do 

    subject { Validatable.new } 

    describe "with a valid email address" do 
    it "should be valid" do 
     addresses = %w[[email protected] [email protected] [email protected] [email protected]] 
     addresses.each do |valid_address| 
     subject.email = valid_address 
     subject.should be_valid 
     end 
    end 
    end 

    describe "with an invalid phone number" do 
    it "should be invalid" do 
     addresses = %w[[email protected],com user_at_foo.org [email protected]] 
     addresses.each do |invalid_address| 
     subject.email = invalid_address 
     subject.should be_invalid 
     end 
    end 
    end 
end 
,536,913,632 10

응용 프로그램/검증/email_validator.rb

require 'mail' 

class EmailValidator < ActiveModel::EachValidator 
    def validate_each(object, attribute, value) 
    begin 
     m = Mail::Address.new(value) 
     # We must check that value contains a domain and that value is an email address 
     r = m.domain && m.address == value 
     t = m.__send__(:tree) 
     # We need to dig into treetop 
     # A valid domain must have dot_atom_text elements size > 1 
     # [email protected] is excluded 
     # treetop must respond to domain 
     # We exclude valid email values like <[email protected]> 
     # Hence we use m.__send__(tree).domain 
     r &&= (t.domain.dot_atom_text.elements.size > 1) 
    rescue => e 
     r = false 
    end 
    object.errors[attribute] << (options[:message] || "is invalid") unless r 
    end 
end 

사용하여 레일 3.2.11, RSpec에 레일 2.11.0

답변

1

귀하의 모델 인스턴스가 유효하지만 당신은 왜 모른다. 이제 오류 메시지가 오류 해시를 인쇄합니다

subject.valid? 
subject.errors.should == {} 

subject.should be_valid 

을 변경해보십시오.

다른 팁 : Don't rescue Exception.

편집

그것은 모두 사양이 실행될 때 정의가 결합 된 Validatable 클래스를 나타납니다. 이 행동이 예상됩니까?

예, 이는 Ruby 클래스에서 일반적입니다. 두 사양 파일이 모두 필요하면 각 Validatable 클래스 본문이 실행되므로 두 가지 유효성 검사가 모두 포함 된 클래스가됩니다.

당신은 어느 과목이 시험받지 않는 검증, 예를 통과함으로써 검증을 분리해야합니다

subject { Validatable.new(:email => "some value") } 

또는 시험에서 유효성 검사에서 특정 오류 메시지에 대한 시험 :

subject.valid? 
subject.errors(:email).should include("is invalid") 

추 신. 진지하게 - 구조를 구출하지 마십시오. 선한 것이 아무것도 없습니다.

+0

팁 주셔서 감사합니다. 두 개의 'Validatable' 클래스 정의가 문제를 일으키는 것으로 보입니다. 내 질문을 업데이트했습니다. –

+0

업데이트 해 주셔서 감사합니다. 스펙이 서로 의존하지 않기를 바라고 각 Validatable 클래스에 고유 한 이름을 지정하려고합니다. 나는 너의 충고를 듣고 구출 예외도 제거했다. –

0

이 문제는 직접 해결할 수 있습니다. 그렇지만 클래스의 이름을 바꿀 수는 있지만 사용하는 솔루션은 사양에 맞는 Validatable 클래스를 만들고 분리하는 것입니다.

는 여기에 코드입니다 :

describe "HttpUriValidator", 
    "Custom validator to ensure URL is a valid URI." do 

    # Create the dummy class once when the test is run. 
    before(:all) do 
    class Validatable 
     include ActiveModel::Validations 
     attr_accessor :url 
     validates :url, http_uri: true 
    end 
    end 
    # Must tearing down the class or it will taint other tests using its 
    # name. 
    after(:all) { Object.send(:remove_const, :Validatable) } 

    subject { Validatable.new } 

편집 :

그냥 머리까지 당신이 (테스트에서 다른 클래스의 네임 피하기 위해) 즉 당신의 테스트 클래스를 랩하는 모듈을 선언한다.

module Foo::Bar 
    describe Something do 
    after(:all) { Foo::Bar.send(:remove_const, :Testable) } 
    end 
end 

그 대신 Object가 아닌 해당 네임 스페이스에서 상수를 제거해야합니다.