2015-02-05 8 views
8

Używam wstawek raw/bare-metal sql, aby zwiększyć wydajność zapisu w mojej usłudze. Mam coś takiego w moim module -Rspec: Testowanie ActiveRecord :: Base.connection.execute

insert = "('#{id}', '#{status}', '#{some_time_val}')" 
sql_string = "INSERT INTO history ('device_id', 'status', 'time') VALUES #{insert}" 
ActiveRecord::Base.connection.execute sql_string 

Kiedy piszę rspec jak poniżej, to sprawdza wszystko z wyjątkiem tego, czy wkładka przeszedł. Tak więc moje oczekiwania nigdy nie zadziałają ze względu na sposób, w jaki rspec, database_cleaner itd. Wycofują transakcje i transakcje. Próbowałem za pomocą

self.use_transactional_fixtures = false 

i

before(:all) do 
    DatabaseCleaner.strategy = nil 
    end 

ale wkładki nadal nie przejść do mojej bazy testowej

describe Worker do 
    let (:device1) {FactoryGirl.create(:device)} 
    let (:device2) {FactoryGirl.create(:device)} 
    let (:device3) {FactoryGirl.create(:device)} 
    self.use_transactional_fixtures = false 

    before(:all) do 
    DatabaseCleaner.strategy = nil 
    end 

    it "does something" do 
    devices = [{"status" => "Offline", "time" => "2013-09-17 18:17:17", "id" => device1.id}, 
       {"status" => "Online", "time" => "2013-09-17 18:18:18", "id" => device2.id}] 
    Worker.any_instance.stubs(:devices).returns(devices) ## Not important for this question 
    Worker.new.perform 

    device1.reload.status.should == "Offline" # FAILS 
    end 

end 

Jak bym to sprawdzić? Jaka jest dobra strategia testowania surowych wstawek sql w ten sposób?

+1

Zdecydowanie nie chcesz czyszczenia bazy danych. Przynajmniej chcesz przyciąć. Brak czyszczenia oznacza zanieczyszczenie testowe.Zauważ również, że twoje zapytanie ma lukę w SQL injection. –

+2

Nie mogę wymyślić powodu, dla którego nie byłbyś w stanie przetestować tego "normalnie" (i myślę, że czyszczenie bazy danych jest czerwonym śledziem). Wykonaj testy, jeśli użyjesz normalnego aktywnego rekordu zamiast "wykonaj" (wydaje mi się ciekawe, że przeładowujesz istniejący obiekt, aby przetestować jego status, podczas gdy opublikowany fragment wydaje się wstawiać nowy wiersz do innej tabeli) –

+0

Co jest wyjściem po uruchomieniu zapytania w konsoli piaskownicy? Wydaje mi się, że w zapytaniu brakuje ostatecznego ";". – cenouro

Odpowiedz

2

Istnieją dwie szkoły myślenia o tym, jak poprawnie przetestować kod: perspektywa "metod" polega na zapewnieniu właściwego zachowania, a perspektywa "wyników" faworyzuje wyniki. Przedstawię obie perspektywy.

Metody:

nie napisałeś ActiveRecord::Base.connection.execute a ty nie jesteś odpowiedzialny za to działa poprawnie. Twój test może skupić się na tym, co przekazałeś do metody execute. Dopasowana specyfikacja może wyglądać mniej więcej tak.

connection = double("Connection") 
expect(ActiveRecord::Base).to receive(:connection) { connection } 
expect(connection).to receive(:execute).with("...") 
Worker.new.perform 

gdzie ... jest SQL, który ma zostać wygenerowany. Sprawdzanie poprawności działania SQL może być wykonane gdzie indziej lub w ogóle nie, w zależności od gustu. Zapewni to (i przyszłych programistów), że poprawna instrukcja została dostarczona do bazy danych.

Wyniki:

Takie podejście nie znajdzie pewność w co podejścia, stwierdzi pewność w wynikach. Jest to podejście, które stosujesz w powyższych przykładach.

Nie pokazujesz nam, jak wygląda metoda perform, więc nie jesteśmy pewni, czy istnieje problem z kodem implementacji, w przeciwieństwie do mechanizmu reload używanego do monitowania ActiveRecord zauważyć zmiany. ActiveRecord ma wiele ulepszeń wydajności zaprojektowanych do chmury bezpośredniego wiązania między zawartością bazy danych a tym, do czego jesteś w stanie uzyskać dostęp na wykresie obiektu Ruby VM. Możesz walczyć z próbą obejścia tego zachowania, jeśli chcesz, ale jeśli chcesz korzystać z ActiveRecord bezpośrednio w swojej implementacji, być może zechcesz to zrobić w swoich testach?

d1_status = ActiveRecord::Base.connection.execute("SELECT status from history where id = #{device1.id} limit 1;").values[0][0] 
expect(d1_status).to eq("Offline") 

Zauważam tutaj pewne kłopotliwe wnioski. Edytujesz tabelę history, ale spodziewasz się wyników z prawdopodobnie modelu Device. Wdrożenie metody status jest również istotne dla twojego problemu. Jeśli wybierzesz to podejście, możesz napisać dodatkowe testy dla metody Device#status.