Testy jednostkowe – bezsensowne wymaganie wydإ‚uإ¼ajؤ…ce pisanie aplikacji. Czy na pewno? Z pewnoإ›ciؤ… taki obraz moإ¼emy bardzo czؤ™sto wynieإ›ؤ‡ z uczelni. Czy to jednak absolutna prawda? Temu zagadnieniu poإ›wiؤ™ciإ‚em prezentacjؤ™ na إپأ³dإ؛ User Ruby Group – czyli comiesiؤ™cznym spotkaniu Railsowcأ³w organizowanym w 6 dzielnicy w إپodzi. Sama prezentacja dostؤ™pna jest w formie PDF w zakإ‚adce â€Materiaإ‚yâ€. Zapraszam na krأ³tkؤ… powtأ³rkؤ™ w formie artykuإ‚u – jeإ›li zaczynasz dopiero przygodؤ™ z Ruby On Rails/programowaniem/testami to ten tekst napisany jest specjalnie dla Ciebie. Dowiesz siؤ™ przede wszystkim â€Po co, do cholery pisaؤ‡ testy?!â€, jak wyglؤ…da napisanie prostego testu w RoR, oraz oczywiإ›cie jak przygotowaؤ‡ sobie do tego إ›rodowisko. No to ruszamy!
Po co, do cholery, pisaؤ‡ testy?
To zdecydowanie najwaإ¼niejszy punkt mojego dzisiejszego artykuإ‚u oraz إ›rodowego LRUGa. إ»ebyإ› dobrze to zrozumiaإ‚ posإ‚uإ¼ؤ™ siؤ™ aplikacjؤ… ktأ³rؤ… rozwijam po godzinach. Warthog Timer ma pomأ³c w zmobilizowaniu siؤ™ przy okazji realizowania rأ³إ¼nych umiejؤ™tnoإ›ci. Sama gإ‚أ³wna funkcjonalnoإ›ؤ‡ jest banalnie prosta. Gdy zaczynasz coإ› robiؤ‡ (na przykإ‚ad pracؤ™ inإ¼ynierskؤ…, aplikacjؤ™ internetowؤ… czy malowanie obrazu) klikasz â€START†na timerze. Gdy koإ„czysz klikasz â€STOP†– i w tym momencie tworzy siؤ™ log_time – jeden obiekt w bazie. Nastؤ™pnie idziesz do statystyk i sprawdzasz ile udaإ‚o ci siؤ™ spؤ™dziؤ‡ czasu nad rأ³إ¼nymi fajnymi rzeczami. Statystykؤ… ktأ³ra nas dzisiaj zajmie bؤ™dzie â€Najlepszy celâ€. Na rysunku poniإ¼ej przedstawiam إ‚aإ„cuch metod jaki musi siؤ™ odbyؤ‡ od pozyskania danych (calculate_time_from_log_times) do obliczenia ostatecznego celu (best_target).
To co warto zauwaإ¼yؤ‡ to fakt, إ¼e ta metoda na samym dole obliczajؤ…ca czas jest uإ¼yta aإ¼â€¦ 8 razy. I to za kaإ¼dym razem jestآ podobny إ‚aإ„cuch. Zastanأ³wmy siؤ™ wiؤ™c co bؤ™dzie, jeإ›li bؤ™dziemy chcieli jؤ… nieco zmieniؤ‡, ale zamiast poprawiؤ‡ – zepsujemy? Oczywiإ›cie zarأ³wno ten, jak i kaإ¼dy inny إ‚aإ„cuch staje siؤ™ kompletnie do niczego. W tym przypadku wiؤ™c Warthog byإ‚by kompletnie sparaliإ¼owanؤ… aplikacjؤ…. â€Ok, co z tego, przecieإ¼ mogؤ™ siؤ™ przeklikaؤ‡â€ ktoإ› powie. Pewnie – ale ile razy? I za kaإ¼dym razem gdy coإ› zmienisz masz zamiar siؤ™ przeklikiwaؤ‡ we wszystkich moإ¼liwych wariantach? Juإ¼ to widzؤ™â€¦
Tutaj z pomocؤ… przychodzؤ… nam testy jednostkowe. Sؤ… to takie maإ‚e fragmenciki kodu, drobne instrukcje ktأ³re majؤ… sprawdzaؤ‡ czy przy odpowiednich parametrach wywoإ‚anie danej metody daje poإ¼ؤ…dany wynik. To rozwiؤ…zanie o wiele lepsze, niإ¼ popularne â€przeklikam siؤ™â€, gdyإ¼ myإ›limy tylko raz (przy pisaniu), a potem po prostu odpalamy testy zawsze gdy dokonamy jakiإ› zmian i.. juإ¼.
Jak to wyglؤ…da w praktyce? Instalujemy potrzebne biblioteki
Poniewaإ¼ na co dzieإ„ piszؤ™ w Ruby on Rails a i prezentacja byإ‚a oparta na tej technologii, to tutaj dokإ‚adnie pokaإ¼ؤ™ jak od 0 zbudowaؤ‡ sobie wygodne إ›rodowisko do pierwszych, najbardziej podstawowych testأ³w. Potrzebne bؤ™dzie nam kilka gemأ³w. Wchodzimy w Gemfile (w gإ‚أ³wnym katalogu aplikacji) i wpisujemy kolejno:
1 2 3 4 5 |
gem â€کrspec-€™ gem â€کfactory_girl_rails’ gem â€کdatabase_cleaner’ |
Gdy juإ¼ to wpiszemy idziemy do terminala i wprowadzamy â€bundle installâ€, co zainstaluje nam wszystkie wyإ¼ej wprowadzone gemy. Krأ³tkie wytإ‚umaczenie – Rspec to gem odpowiedzialny za obsإ‚ugؤ™ testأ³w, jest moإ¼na powiedzieؤ‡ â€gإ‚أ³wnym filarem†ktأ³ry wszystko spaja. Poniewaإ¼ jednak jest to إ›rodowisko testowe a nie development, musimy stworzyؤ‡ tam nasze obiekty. Do tego posإ‚uإ¼y nam Factory Girl (Nie pytaj, nazwy tutaj naprawdؤ™ bywajؤ… dziwne…). Na koniec bardzo przydatna sprawa – Database Cleaner pomoإ¼e nam czyإ›ciؤ‡ bazؤ™ przed kaإ¼dym testem. Dziؤ™ki temu na kaإ¼dy test popatrzymy jako osobnؤ… czؤ™إ›ؤ‡ i zachowamy nad nim peإ‚nؤ… kontrolؤ™. Skoro juإ¼ zainstalowaإ‚eإ› gemy, wejdإ؛ ponownie w terminal i wprowadإ؛ komendؤ™ rails generate rspec:install. Po tym zabiegu powinieneإ› w gإ‚أ³wnym katalogu aplikacji mieؤ‡ folder â€specâ€. Na ten moment wpisz w terminal â€rspec†i… zobacz jak إ›wietnie wykonaإ‚y Ci siؤ™ twoje wszystkie (0) testy. Gratuluje! Wejdإ؛ w folder spec, a nastؤ™pnie spec_helper.rb. tutaj wklej nastؤ™pujؤ…cy kod ktأ³ry obsإ‚uإ¼y czyszczenie bazy danych w odpowiednich momentach.
Piszemy test!
Zabierzmy siؤ™ za napisanie testu. Tutaj mogؤ™ niestety jedynie pokazaؤ‡ Ci jak wyglؤ…da to u mnie, w Warthog’u. Testujemy omawianؤ… wyإ¼ej metodؤ™ calculate_time_from_log_times. Wyglؤ…da ona nastؤ™pujؤ…co:
1 2 3 4 5 6 7 |
def calculate_time_from_log_times(log_times) time = 0 log_times.each do |log_time| time += log_time.time * 60 end time end |
Poniewaإ¼ znajduje siؤ™ ona w serwisie (taka klasa w ktأ³rej umieszczamy logikؤ™ – na przykإ‚ad rأ³إ¼ne algorytmy), stworzyإ‚em w folderze â€spec†nowy katalog – â€servicesâ€, zaإ› w nim â€targets_stats_service_spec.rb†– czyli nazwؤ™ mojego serwisu z dodanym na koإ„cu â€_spec†(To kwestia konwencji, ktأ³ra w Ruby on Rails jest إ›wiؤ™ta! Dlatego teإ¼ siؤ™ do niej stosuj).آ Wewnؤ…trz napisaإ‚em 3 testy – widoczne pod spodem. Zwrأ³ؤ‡my uwagؤ™ na kilka elementأ³w:
- Context – opisuje pewien kontekst naszych prac. Ja napisaإ‚em jakؤ… metodؤ™ w nim bؤ™dؤ™ testowaإ‚.
- It – ten blok to wإ‚aإ›nie test jednostkowy. Tutaj opisujemy juإ¼ konkretniejsze dziaإ‚anie – np. jakؤ… liczbؤ™ powinna zwrأ³ciؤ‡ metoda.
- Expect(coإ›tam).to eq(coإ›tam) – oczekiwany wynik testu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
require 'rails_helper' context 'tests for calculate_time_from_log_times' do it 'should get time 0' do user = FactoryGirl.create(:user) service = DirectData::TargetsStatsService.new(user.id) time = service.calculate_time_from_log_times(user.log_times) expect(time).to eq(0) end it 'should get time 1800 (30 min * 60 s)' do user = FactoryGirl.create(:user) prefab = FactoryGirl.create(:prefab, user: user) FactoryGirl.create(:skill, user: user) FactoryGirl.create(:log_time, user: user, prefab: prefab) service = DirectData::TargetsStatsService.new(user.id) time = service.calculate_time_from_log_times(user.log_times) expect(time).to eq(1800) end it 'should get time 4500 (30 + 30 + 15 [min] * 60 s)' do user = FactoryGirl.create(:user) prefab = FactoryGirl.create(:prefab, user: user) FactoryGirl.create(:skill, user: user) FactoryGirl.create(:log_time, user: user, prefab: prefab) FactoryGirl.create(:log_time, user: user, prefab: prefab) FactoryGirl.create(:log_time2, user: user, prefab: prefab) service = DirectData::TargetsStatsService.new(user.id) time = service.calculate_time_from_log_times(user.log_times) expect(time).to eq(4500) end end |
Warto zwrأ³ciؤ‡ uwagؤ™ na to FactoryGirl.create(). To wإ‚aإ›nie stworzenie obiektu w bazie. إ»eby stworzyؤ‡ â€matrycؤ™â€ takiego obiektu najpierw utworzyإ‚em folder â€factories†wewnؤ…trz â€specâ€, a nastؤ™pnie stworzyإ‚em odpowiedniki modeli – np. log_time.rb, target.rb czy user.rb. Pokazujؤ™ przykإ‚adowy kod – user.rb إ¼ebyإ› wiedziaإ‚ jak tworzyؤ‡ proste Factory Girl.
1 2 3 4 5 6 7 |
FactoryGirl.define do factory :user do password 'lolopolo' confirmed_at { DateTime.now } end end |
Gdy juإ¼ napisaإ‚eإ› testy, jedyne co musisz zrobiؤ‡ aby je odpaliؤ‡ to wejإ›ؤ‡ do terminala i wpisaؤ‡â€¦ rspec. Klikasz enter i juإ¼! Testy samoczynnie idؤ…, zaإ› na koniec â€wypluwajؤ…†raport. Jeإ›li ktأ³ryإ› siؤ™ nie powiأ³dإ‚ to prawdopodobnie masz coإ› nie tak z metodؤ… (lub z samym testem). Gdy uda Ci siؤ™ doprowadziؤ‡ wszystko do pozytywnego stanu moإ¼esz poklepaؤ‡ siؤ™ po pleckach – wykonaإ‚eإ›(/إ‚aإ›!) kawaإ‚ dobrej roboty!
Co dalej?
Na koniec chciaإ‚bym bardzo mocno zaznaczyؤ‡, إ¼e testy to gigantyczny temat ktأ³ry zaledwie tutaj musnؤ™liإ›my. Zachؤ™cam Ciؤ™ do eksplorowania Internetu w celu dalszego szlifowania umiejؤ™tnoإ›ci zwiؤ…zanych testowaniem. Pamiؤ™taj, إ¼e od tego zaleإ¼y jakoإ›ؤ‡ twojej aplikacji, a przecieإ¼ nie chcemy robiؤ‡ niczego byle jak prawda? Testy oszczؤ™dzajؤ… mnأ³stwo czasu i nerwأ³w oraz dajؤ… Ci wiؤ™kszؤ… kontrolؤ™ nad tym co dzieje siؤ™ z twojؤ… aplikacjؤ…. Oczywiإ›cie jeإ›li bؤ™dziesz chciaإ‚ poznawaؤ‡ te tematy, sإ‚uإ¼ؤ™ pomocؤ… – moإ¼esz napisaؤ‡ do mnie maila lub zإ‚apaؤ‡ mnie na ktأ³rymإ› z LRUG’أ³w;-)
P.S.
Warthog jest w wersji pre-alfa, ale jeإ›li chcesz sprأ³bowaؤ‡ przejؤ…ؤ‡ kontrolؤ™ nad czasem ktأ³ry poإ›wiؤ™casz na rzeczy ktأ³re Ciؤ™ budujؤ…, to zachؤ™cam do korzystania na stronie warthogtimer.com. Przed rejestracjؤ… po prawej stronie zajrzyj w znak zapytania ktأ³ry podpowie Ci, jak korzystaؤ‡ z serwisu. A jeإ›li bؤ™dziesz miaإ‚ uwagi, pytania lub sugestie rأ³wnieإ¼ odnoإ›nie rozwoju aplikacji – zamieniam siؤ™ w sإ‚uch : – )
Ja nazywam siؤ™ Marek Czuma, آ aآ to jest IT-Blog Wolnego Czإ‚owieka
piszؤ™ do Ciebie prosto z إپodzi