2013-12-18 5 views
8

Sagen Sie, ich habe Modelle User und Post, ein Benutzer has_many Beiträge und ein Beitrag belongs_to ein Benutzer.Beschleunigen Verbindungen in Modellspezifikationen mit FactoryGirl - Erstellen vs Build vs Build_stubbed

Wenn ich eine Spezifikation für Post, mein erster Instinkt schreiben ist, so etwas zu schreiben:

before do 
    @user = FactoryGirl.create :user 
    @post = @user.posts.new(title: "Foo", content: "bar) 
end 

... tests for @post go here ... 

Aber dies wird einen neuen Benutzer erstellen - Schlagen die Datenbank - für jeden einzelnen Test, das ist Dinge verlangsamen. Gibt es einen besseren Weg, um meine Tests zu beschleunigen und zu vermeiden, die DB so oft zu treffen?

Wie ich es verstehe, kann ich FactoryGirl.build :user nicht, weil, auch wenn es nicht die DB getroffen wird, werden die Verbände nicht richtig, da @user nicht eine ID und so wird @post.user nicht (es gibt nil.)

I FactoryGirl.build_stubbed :user, die ein erstellt verwenden könnte @user „fake beibehalten“, die eine ID hat, aber @post.user gibt noch gleich Null. Hat build_stubbed irgendeinen praktischen Vorteil gegenüber build, wenn ich Dinge im Zusammenhang mit Assoziationen teste?

Ich nehme an, ich könnte build_stubbed Stub @post.user verwenden, so dass es @user zurückgibt ... gibt es einen Grund, warum dies eine schlechte Idee sein könnte?

Oder sollte ich einfach create verwenden und den Geschwindigkeits-Treffer akzeptieren?

Die einzige andere Alternative, die ich mir vorstellen kann, wäre, @user in einem before(:all) Block einzurichten, der wie eine schlechte Idee scheint.

Was ist der beste Weg, um diese Art von Tests auf eine saubere, prägnante Weise zu schreiben, die vermeidet, zu viele DB-Abfragen zu machen?

Antwort

18

Wenn Sie nicht wollen, dass Ihre Tests die Datenbank treffen, müssen Sie dies tun.

before do 
    @user = FactoryGirl.build_stubbed :user 
    @post = FactoryGirl.build_stubbed :post 
    @user.stub(:posts).and_return([@post]) 
    @post.stub(:user).and_return(@user) 
end 

Hinweis: Seien Sie vorsichtig, wenn Sie before(:all) verwenden. Es wird nicht in einer Transaktion ausgeführt. Was auch immer Sie in before(:all) erstellen, wird in der Datenbank zurückgelassen und kann einen Konflikt mit anderen Tests verursachen

Über FactoryGirl.build, baut es das Objekt, erstellt aber die Assoziationen.

Für zB:

factory :user do 
    association posts 
end 

FactoryGirl.build(:user) #this creates posts in the database even though you are only building the parent object(user) 
15

Kurzantwort

@user = FactoryGirl.build_stubbed(:user) 
@post = FactoryGirl.build_stubbed(:post, :user => @user) 

Dies wird ohne jemals schlagen die Datenbank @ post.user Arbeit machen.

Lange Antwort

Meine Empfehlung auf dem before Block zu warten wäre, bis Sie sicher, dass Sie es brauchen. Erstellen Sie stattdessen die Daten, die Sie für jeden einzelnen Test benötigen, und extrahieren Sie die Duplizierung in Methoden oder neue Fabriken, wenn Sie sie finden.

Müssen Sie tatsächlich in jedem einzelnen Test auf den Benutzer verweisen? Dass @user in jedem Test verfügbar ist, sagt anderen Entwicklern, dass es überall wichtig ist.

Schließlich, vorausgesetzt, dass die Benutzerzuordnung auch in Ihrer Post-Factory deklariert ist, erhalten Sie automatisch eine funktionierende post.user, wenn Sie build_stubbed(:post) tun.

8

Es kann leicht sein, die Unterschiede zwischen create, build und build_stubbed zu vergessen. Hier finden Sie eine Kurzreferenz für Personen in der gleichen Situation (da diese Seite in den Suchergebnissen weit oben steht).

factory :post do 
    association :user, factory: :user, strategy: :build 
end 

, so dass Sie build ein post ohne sparen:

# Returns a User instance that's not saved (does not write to DB) 
user = build(:user) 

# Returns a saved User instance (writes to DB) 
user = create(:user) 

# Returns a hash of attributes that can be used to build a User instance 
attrs = attributes_for(:user) 

# Returns an object with all defined attributes stubbed out 
stub = build_stubbed(:user) 

# Passing a block to any of the methods above will yield the return object 
create(:user) do |user| 
    user.posts.create(attributes_for(:post)) 
end 

Source

1

Aus dem Dokument der Fabrik Mädchen, können Sie Strategie build für user in Verbindung in post Fabrik wie folgt identifizieren user

post = build(:post) 
post.new_record?  # => true 
post.author.new_record? # => true