2013-08-09 16 views
7

W naszej aplikacji Rails 4.0 używającej MySql używamy rspec razem z gemem database_cleaner skonfigurowanym ze strategią: transakcja do czyszczenia naszej bazy danych dla każdego przypadku testowego. Jeśli mamy niestandardowe transakcje, które powinny być wycofane, to nie działa.Niestandardowa transakcja nie działa z database_cleaner w rspec

Bez database_cleaner klejnot i tylko przy użyciu standardowego sposobu:

config.use_transactional_fixtures = true 

wszystko działa jak aspected. Ale do uruchamiania testów cech z JavaScriptem potrzebujemy database_cleaner, aby zmienić strategię usuwania fixingu na: przycinanie.

Jak korzystać z database_cleaner razem z transakcjami niestandardowymi i dlaczego różni się od standardowej strategii transakcji rspec?

+0

Jestem pewien, że wiesz o: DatabaseCleaner.strategy =: deletion/DatabaseCleaner.strategy =: skrócenie, o czym wspomniałeś sam. Wciąż mnie to interesuje, jeśli jest to problem w strategii database_cleaner: transakcja, lub jeśli jest to tylko ogólny problem z zagnieżdżonymi transakcjami, które czasami zachowują się nieoczekiwanie. –

Odpowiedz

11

Problem polega na tym, że database_cleaner wywołuje ActiveRecord.rollback po zakończeniu testu - którego używasz również w swoim kodzie. InnoDB/MySQL nie obsługuje prawdziwych transakcji zagnieżdżonych, więc transakcje zagnieżdżone w kodzie nie są traktowane tak naprawdę, jak transakcje, chyba że są do tego wyraźnie wywołane.

Rozważmy ten blok (od docs ActiveRecord):

User.transaction do 
    User.create(username: 'Kotori') 
    User.transaction do 
    User.create(username: 'Nemu') 
    raise ActiveRecord::Rollback 
    end 
end 

czego można się spodziewać się dziać po wywołaniu Wycofywanie? Spodziewacie się wycofać użytkownika Nemu i zostawić was z Kotori, prawda? Cóż, tak naprawdę dzieje się tak, że zarówno Kotori, jak i Nemu są stworzeni. Wycofanie nie uruchamia się, ponieważ znajdujesz się w transakcji zagnieżdżonej (a AR tylko dba o jednostkę nadrzędną, obecnie), a transakcja nadrzędna (która ma faktyczną transakcję db) nie widzi wywołania wycofania - jak jest wywoływana izolowany blok. To dziwne.

Rozwiązanie:

Klass.transaction(requires_new: true) 

Jeśli requires_new ustawiony ActiveRecord użyje lub pseudo-używać zagnieżdżonych transakcji (dla PostgreSQL to będzie dokonywać transakcji zagnieżdżonych, MySQL/InnoDB uczyni to punkty zapisu). Kiedy wywołasz wycofanie z ActiveRecord, wykrywa on jego zasięg i wydaje prawidłowe wycofywanie.

Drugim rozwiązaniem jest użycie skracania lub usuwania jako strategii testów związanych z transakcjami.

+0

świetne wyjaśnienie. dzięki! –