Testowanie złożonego zapytania bez railsów

Hej,
załóżmy że mam coś takiego:

Order. where('created_at > ? and created_at < ?', start, end) select('some, few, fields'). for_user(user). some_other_scope
Jak to przetestować w duchu Object on Rails, prezentacji Corey-a Haines-a i postów blogowych solnic-a? Mam teraz dobre testy, które niestety są wolne, bo tworzą rekordy (3-5) na każdy test. Wg mnie tak jest pragmatyczniej, ale może ktoś ma jakiś fajny pomysł?

Właśnie pomyślałem o fixtures i before(:all) - ale to ciągle używa AR.

ja staram się testować w taki sposób:

[code]my_scope = Order.where(‘created_at > ? and created_at < ?’, start, end).select(‘some, few, fields’).for_user(user)

assert_equal [“some, few, fields”], my_scope.select_values
assert_equal “sql string”, my_scope.where_values[/code]
Nie potrzebujesz wtedy tworzyć ani jednego rekordu – nie testujesz AR, czy pobiera odpowiednie rekordy i je dobrze mapuje, tylko twoje scopy, czy tworzą poprawny sql.

Jedyna opcja żeby to na prawdę przetestować to tworząc rekordy w bazie, czy to z factory girl czy z fixtures. Testów jednostkowych do tego raczej nie napiszesz. No dobra, możesz: sprawdzić czy zapytanie SQL wygenerowane będzie takie jak potrzebujesz, ale moim zdaniem jest to trochę bez sensu bo nie wiesz czy ono się wykona poprawnie czy nie.

Moim zdaniem, jesteś w stanie sprawdzić, w sposób pokazany powyżej. Jeśli zapytanie nie wykona się poprawnie, to wysypie Ci się test wyższego poziomu.

Moim zdaniem nie da się testować zapytań do bazy bez użycia bazy. To jest bez sensu. Zastanów się np. co by się stało jeśli podmieniłbyś silnik bazodanowy, który w drobnych aspektach działałby inaczej niż ten poprzedni. Taki test nigdy Ci tego nie powie.
Polecam screencast Avdiego The end of mocking

Widzę, że czytamy tych samych guru :slight_smile:

nie pamiętam kiedy ostatnio zmieniałem silnik bazodanowy w projekcie, poza przejściem na postgresa na wczesnym etapie rozwoju każdej aplikacji (ale to troszkę inny temat).

znam :wink:

Uważam, że proste scopy można testować przez where_values (zwłaszcza, gdy mówimy o scopach w stylu for_user (strzelam: “WHERE user_id = ?”), published (“WHERE published = ture”). Testowanie skomplikowanych scopów w taki sposób – zgoda, mija się z celem.

[quote]Jak to przetestować w duchu Object on Rails, prezentacji Corey-a Haines-a i postów blogowych solnic-a?
Widzę, że czytamy tych samych guru :-)[/quote]
Widzę, że jest nas więcej :wink:

Problem w tym, że ciężko jest ocenić kiedy ten scope staje się bardziej skomplikowany. Oraz drugi, większy problem: nie mając testów integracyjnych ciężej jest wyłapać błędy, które na produkcji Cię będą dużo kosztować. Wczoraj np. strzeliłem takiego babola: https://github.com/travis-ci/travis-core/commit/29d93c87a4bd1aab8bbc3d760a0ec218a0ceb8ff, którego wyłapało CI po uruchomieniu całego test suitu. Ja wiem, dumbest thing ever i w ogóle weź, ale może to się zdarzyć każdemu. Swoją drogą zorientowałem się o co chodzi dopiero po przespaniu się z problemem - jeszcze jedna lekcja do: jak masz trudny problem, którego nie możesz ruszyć to go zostaw i rano rozwiążesz w 5 minut :wink:

Ogólnie moje zdanie jest takie, że szybkie testy są fajne, ale nic nie zastąpi testów integracyjnych. Jeżeli przeraża kogoś, że testy się długo wykonują, to można podzielić test suite i szybkie testy wykonywać u siebie, a integracyjne tylko na CI i traktować integracyjne jako dopełnienie, ale rezygnacja z dotykania bazy przy scope’ach jest imho kiepski pomysłem.

Co do szybkości takich testów - fixtures są dużo szybsze niż factories z reguły.

Dokładnie to samo mówię moim studentom. Żeby to osiągnąć warto jednak wszelkie zapytania, w których dotykamy bazy zamknąć wewnątrz metod biznesowych, tzn. nigdy nie wywoływać na zewnątrz np. user.projects.where(“data_due > ?”…) tylko zrobić metodę user#outdated_projects. Wtedy w szybkich testach taką metodę można sobie wystubować, a w wolnych testach przetestować jej faktyczne działanie.

Są też ciągle niedoceniane. Ciągle się spotykam z takim podejściem - a po co ci fixtures, jak masz factories. Jak się używa w ymlach erba, asocjacji i innych tego typu ułatwień (dawno temu fixtures tego nie miały i to rzeczywiście było cięzkie), to można naprawdę dużo zyskać, nie tylko na szybkości wykonywania, ale i pisania. A już tym bardziej, jeśli ktoś używa fabryk do tworzenia na przykład województw, to powinien się poważnie zastanowić, czy rzeczywiście używa dobrego narzędzia.

Dokładnie to samo mówię moim studentom. Żeby to osiągnąć warto jednak wszelkie zapytania, w których dotykamy bazy zamknąć wewnątrz metod biznesowych, tzn. nigdy nie wywoływać na zewnątrz np. user.projects.where(“data_due > ?”…) tylko zrobić metodę user#outdated_projects. Wtedy w szybkich testach taką metodę można sobie wystubować, a w wolnych testach przetestować jej faktyczne działanie.[/quote]
+1, tutaj dochodzi jeszcze fakt, że podświadomie ludzie myślą, że skoro jeden scope działa i drugi scope działa, to jak te scope’y połączą, to też będzie działać :wink: Co niestety często nie jest prawdą.

“często”, hehehe. nawet więcej, scope’y dotykające tych samych atrybutów – a jest ich więcej niż się każdemu zdaje (paginacja!) – działają, delikatnie mówiąc, różnie.
Link pierwszy:
http://blog.envylabs.com/post/52395695243/chaining-scopes-in-rails-4
Link drugi:

A od Rails4 będą działały inaczej:
Link trzeci:
https://github.com/amatsuda/kaminari/pull/307