2016-04-12 19 views
1

Ich habe eine Rails4 App mit rspec + factory_girl. Ich möchte die Validierung testen, die sicherstellt, dass das Produkt mindestens eine Funktion, Wettbewerb, Anwendungsfall und Industrie hat. Die ersten 3 müssen zu einem Produkt gehören, aber die Industrie kann eigenständig existieren. Ich habe noch nicht versucht, die Industrie in den Test zu versetzen, da ich nicht einmal die ersten 3 arbeiten kann.rspec + Fabrikmodell Test verschachtelte Attribute

Ich versuchte den Ansatz unten, in dem ich eine Produktfabrik erstellen, die product_feature, product_competition und product_usecase hat. Aus irgendeinem Grund funktioniert es nicht.

Benutze ich den richtigen Ansatz? Wenn ja, was ist falsch an meinem Code?

1) Product nested attribute validation has a valid factory 
    Failure/Error: expect(create(:product_with_nested_attrs)).to be_valid 

    ActiveRecord::RecordInvalid: 
     Validation failed: You have to choose at least 1 industry., You must have at least 1 product feature., You must name at least 1 competition., You must describe at least 1 usecase. 

product.rb (AKTUALISIERT)

belongs_to :user 
has_many :industry_products, dependent: :destroy, inverse_of: :product 
has_many :industries, through: :industry_products #industry exists without product; connected with has_many thru association 
has_many :product_features, dependent: :destroy 
has_many :product_competitions, dependent: :destroy 
has_many :product_usecases, dependent: :destroy 

accepts_nested_attributes_for :industry_products, reject_if: :all_blank, allow_destroy: true 
accepts_nested_attributes_for :product_features, reject_if: :all_blank, allow_destroy: true 
accepts_nested_attributes_for :product_competitions, reject_if: :all_blank, allow_destroy: true 
accepts_nested_attributes_for :product_usecases, reject_if: :all_blank, allow_destroy: true 

#UPDATE 

validate :product_features_limit #Also have it for product_usecases and product_competititons 

def product_features_limit 
    if self.product_features.reject(&:marked_for_destruction?).count > 10 
    self.errors.add :base, "You can't have more than 10 features." 
    elsif self.product_features.reject(&:marked_for_destruction?).count < 1 
    self.errors.add :base, "You must have at least 1 product feature." 
    end 
end 

Fabrik

FactoryGirl.define do 

    factory :product_competititon do 
    competitor { Faker::Commerce.product_name } 
    differentiator { Faker::Lorem.paragraph } 
    product 
    end 

    factory :product_feature do 
    feature { Faker::Lorem.paragraph } 
    product 
    end 

    factory :product_usecase do 
    example { Faker::Lorem.sentence } 
    detail { Fakert::Lorem.paragraph } 
    product 
    end 

    factory :product do 
    name { Faker::Commerce.product_name } 
    company { Faker::Company.name } 
    website { 'https://example.com' } 
    oneliner { Faker::Lorem.sentence } 
    description { Faker::Lorem.paragraph } 
    user 

    factory :product_with_nested_attrs do 
     transient do 
     nested_attrs_count 1 
     end 
     after(:create) do |product, evaluator| 
     create_list(:product_feature, evaluator.nested_attrs_count, product: product) 
     create_list(:product_competititon, evaluator.nested_attrs_count, product: product) 
     create_list(:product_usecase, evaluator.nested_attrs_count, product: product) 
     end 
    end 
    end 
end 

product_spec.rb

RSpec.describe Product, type: :model do 

    describe "nested attribute validation" do 

    it "has a valid factory" do 
     expect(create(:product_with_nested_attrs).to be_valid 
    end 

    end 
end 
+0

Nicht featureful genug, um eine vollständige Antwort zu tun: Sie müssen eine create_list nicht tun, können Sie so etwas wie tun: 'product.product_features << create (: product_feature, product: product) 'wickle einen' x.times {} 'Block darum herum, wenn du eine bestimmte Anzahl benötigst. –

+0

Jim, was meinst du mit nicht vollgestopft genug? Können Sie mir sagen, warum es in diesem Fall funktioniert und warum es hier nicht mit diesem Code funktioniert? –

+0

Normalerweise sichere ich meine Antworten mit Verweisen auf die API-Dokumentation. In diesem Fall fühlte ich, dass es "schnell und schmutzig" war. –

Antwort

1

ein Juwel Es ist shoulda (https://github.com/thoughtbot/shoulda), die Sie testen lassen accepts_nested_attributes_for (Validierungen und Assoziationen) direkt mit einem einzigen Matcher. Aber wenn Sie das Verhalten (was) lieber als die Implementierung (wie) testen möchten, können Sie etwas wie folgt tun ...

Genau wie das, was ich zuvor erwähnte (setting up objects for model testing with factory_girl), würde ich zuerst die Assoziationen aus den Fabriken entfernen.

Fabriken

factory :product_competititon do 
    competitor { Faker::Commerce.product_name } 
    differentiator { Faker::Lorem.paragraph } 
end 

factory :product_feature do 
    feature { Faker::Lorem.paragraph } 
end 

factory :product_usecase do 
    example { Faker::Lorem.sentence } 
    detail { Fakert::Lorem.paragraph } 
end 

factory :product do 
    name { Faker::Commerce.product_name } 
    company { Faker::Company.name } 
    website { 'https://example.com' } 
    oneliner { Faker::Lorem.sentence } 
    description { Faker::Lorem.paragraph } 
end 

Specs

RSpec.describe Product, type: :model do 

    describe "validation" do 
    let(:user) { create(:user) } 

    it "should be valid if a product has at least one competition, feature, usecase and industry" do 
     attr = attributes_for(:project).merge({ 
     user_id: user.id, 
     product_competitions: [attributes_for(:project_competition)], 
     product_features: [attributes_for(:project_feature)], 
     product_usecases: [attributes_for(:project_usecase)], 
     product_industries: [attributes_for(:industry)], 
     }) 
     expect(Product.new(attr)).to be_valid 
    end 

    it "should be invalid if no competition is given" do 
     attr = attributes_for(:project).merge({ 
     user_id: user.id, 
     product_features: [attributes_for(:project_feature)], 
     product_usecases: [attributes_for(:project_usecase)], 
     product_industries: [attributes_for(:industry)], 
     }) 
     expect(Product.new(attr)).to be_invalid 
    end 

    it "should be invalid if no feature is given" do 
     attr = attributes_for(:project).merge({ 
     user_id: user.id, 
     product_competitions: [attributes_for(:project_competition)], 
     product_usecases: [attributes_for(:project_usecase)], 
     product_industries: [attributes_for(:industry)], 
     }) 
     expect(Product.new(attr)).to be_invalid 
    end 

    # ... similar test cases for usecase and industry validation ... 

    end 
end 
+0

Sibevin, ich benutze "shofa-matchers" mit den verschachtelten Attras. Prüft das nicht nur, ob im Modell "accept_nested_attributes_for" gesetzt ist? Mit diesem Code versuche ich zu testen, ob das erstellte Produkt mindestens ein 'product_feature',' product_usecase', etc. hat. Mein 'product.rb' wurde mit dem Validator aktualisiert. –

+0

Sibevin, bitte überprüfen Sie auch meine vorherigen Kommentar. Also spielte ich mit diesem Code herum und bekam 'ActiveRecord :: AssociationTypeMismatch: ProductFeature (# 70333990300640) erwartet, bekam Hash (# 70333940263240)' Fehler. Ich schätze also, dass das Produkt, das noch nicht erstellt wurde, an die 'attributes_for (: product_feature, product: #this product) 'übergeben werden sollte, aber keine Ahnung wie. Ich denke, es ist wie ein Hühnerei Problem, da sie sofort erstellt werden müssen. Also, wenn ich versuche, Produkt zuerst zu erstellen, dann muss mindestens 1 Feature Fehler erstellt werden und wenn versuchen, das Feature zu erstellen, dann muss Produkt Fehler definiert werden. –

+0

@SzilardMagyar Dein erster Kommentar ist richtig, daher bevorzuge ich es, das Verhalten anstatt der Implementierung zu testen. (Obwohl die shofaa den ähnlichen Weg verwenden, um seinen Matcher zu implementieren). –