Imanel: Pingwin w sieci

Testowanie Juggernauta za pomocą Cucumbera i Selenium

Zazwyczaj pisząc testy stawiamy na sprawdzone, konsolowe rozwiązania typu RSpec czy CucumberWebrat – dzięki temu łączymy szybkość i wygodę. Niestety pisząc niektóre aplikacje internetowe nie da się uniknąć pobrudzenia sobie rąk. I o ile możemy sobie pozwolić na narzędzia typu Celerity jeśli operujemy wyłącznie javascriptem, o tyle kiedy dochodzą technologie typu flash czy java trzeba sięgnąć po większy kaliber. W takich momentach przychodzi nam na pomoc Selenium – framework do testowania aplikacji w prawdziwych przeglądarkach. Jak połączyć Selenium z Cucumberem nie będę tu opisywał bo wszystko jest dokładnie wyjaśnione na wiki Cucumbera.

Domyślnie Cucumber otwiera jedną przeglądarkę i w niej wszystko testuje. Czasami jednak potrzebujemy kilku jej instancji – jak np. w przypadku stron gdzie potrzebna jest natychmiastowa komunikacja między dwoma użytkownikami. Na szczęście skrypty do Selenium w Cucumberze łatwo obejść i otworzyć drugą przeglądarkę, a potem się między nimi przełączać. Wystarczy w env.rb(albo odpowiednim pliku dla selenium) wpisać:

1
2
3
$browser2 = Webrat::SeleniumSession.new.selenium
$browser = nil
$browser1 = Webrat::SeleniumSession.new.selenium

Druga linijka jest niezbędna aby Cucumber poprawnie zainicjalizował drugą przeglądarkę. Potem już wystarczy tylko utworzyć odpowiednie kroki dla selenium – na przykład:

Given /^I am using first browser$/ do
  if Webrat.configuration.mode == :selenium
    $browser = $browser1
  end
end
 
Given /^I am using second browser$/ do
  if Webrat.configuration.mode == :selenium
    $browser = $browser2
  end
end

Domyślnie aktywowana będzie pierwsza przeglądarka, ale należy pamiętać że ostatnio używana instancja nie jest restartowana pomiędzy poszczególnymi testami więc zawsze należy powrócić do pierwszej przeglądarki na zakończenie testu w którym zmieniamy okna. Dodatkowo przydatne są jeszcze dwa kroki:

When /^I wait for page to load$/ do
  if Webrat.configuration.mode == :selenium
    selenium.wait_for_page_to_load
  end
end
 
Given /^I wait for juggernaut$/ do
  if Webrat.configuration.mode == :selenium
    sleep(3)
  end
end

Pierwszy zapewnia że załadowane zostaną wszystkie skrypty przed przejściem do następnych kroków, a drugi daje Juggernautowi chwilę na dostarczenie wiadomości. Z tak skonfigurowanymi testami możemy używać wszystkich kroków dostarczonych przez Cucumbera bez martwienia się o kompatybilność.

Sphinx a sprawa polska

Pisząc aplikacje internetowe czasami spotykam się z sytuacją kiedy trzeba wykorzystać full text search. W takich momentach niezastąpione są narzędzia typu Sphinx czy Ferret. Osobiście preferuję Sphinxa za jego szybkość i wygodę. Czasem jednak potrafi sprawić problemy – szczególnie z wyszukiwaniem akcentów w językach. Otóż domyślnie kodowanie UTF-8 nie jest odpowiednio konwertowane i Sphinx traktuje znaki z poza ASCII jako znaki rozdzielające wyrazy. W necie można znaleźć wiele stron z prostym rozwiązaniem tego problemu – wystarczy w pliku konfiguracyjnym dodać odpowiednie reguły konwersji:

charset_table: "0..9, a..z, _, A..Z->a..z, U+00C0->a, U+00C1->a, U+00C2->a, U+00C3->a, U+00C4->a, U+00C5->a, U+00C7->c, U+00C8->e, U+00C9->e, U+00CA->e, U+00CB->e, U+00CC->i, U+00CD->i, U+00CE->i, U+00CF->i, U+00D1->n, U+00D2->o, U+00D3->o, U+00D4->o, U+00D5->o, U+00D6->o, U+00D9->u, U+00DA->u, U+00DB->u, U+00DC->u, U+00DD->y, U+00E0->a, U+00E1->a, U+00E2->a, U+00E3->a, U+00E4->a, U+00E5->a, U+00E7->c, U+00E8->e, U+00E9->e, U+00EA->e, U+00EB->e, U+00EC->i, U+00ED->i, U+00EE->i, U+00EF->i, U+00F1->n, U+00F2->o, U+00F3->o, U+00F4->o, U+00F5->o, U+00F6->o, U+00F9->u, U+00FA->u, U+00FB->u, U+00FC->u, U+00FD->y, U+00FF->y, U+0100->a, U+0101->a, U+0102->a, U+0103->a, U+0104->a, U+0105->a, U+0106->c, U+0107->c, U+0108->c, U+0109->c, U+010A->c, U+010B->c, U+010C->c, U+010D->c, U+010E->d, U+010F->d, U+0112->e, U+0113->e, U+0114->e, U+0115->e, U+0116->e, U+0117->e, U+0118->e, U+0119->e, U+011A->e, U+011B->e, U+011C->g, U+011D->g, U+011E->g, U+011F->g, U+0120->g, U+0121->g, U+0122->g, U+0123->g, U+0124->h, U+0125->h, U+0128->i, U+0129->i, U+012A->i, U+012B->i, U+012C->i, U+012D->i, U+012E->i, U+012F->i, U+0130->i, U+0134->j, U+0135->j, U+0136->k, U+0137->k, U+0139->l, U+013A->l, U+013B->l, U+013C->l, U+013D->l, U+013E->l, U+0143->n, U+0144->n, U+0145->n, U+0146->n, U+0147->n, U+0148->n, U+014C->o, U+014D->o, U+014E->o, U+014F->o, U+0150->o, U+0151->o, U+0154->r, U+0155->r, U+0156->r, U+0157->r, U+0158->r, U+0159->r, U+015A->s, U+015B->s, U+015C->s, U+015D->s, U+015E->s, U+015F->s, U+0160->s, U+0161->s, U+0162->t, U+0163->t, U+0164->t, U+0165->t, U+0168->u, U+0169->u, U+016A->u, U+016B->u, U+016C->u, U+016D->u, U+016E->u, U+016F->u, U+0170->u, U+0171->u, U+0172->u, U+0173->u, U+0174->w, U+0175->w, U+0176->y, U+0177->y, U+0178->y, U+0179->z, U+017A->z, U+017B->z, U+017C->z, U+017D->z, U+017E->z, U+01A0->o, U+01A1->o, U+01AF->u, U+01B0->u, U+01CD->a, U+01CE->a, U+01CF->i, U+01D0->i, U+01D1->o, U+01D2->o, U+01D3->u, U+01D4->u, U+01D5->u, U+01D6->u, U+01D7->u, U+01D8->u, U+01D9->u, U+01DA->u, U+01DB->u, U+01DC->u, U+01DE->a, U+01DF->a, U+01E0->a, U+01E1->a, U+01E6->g, U+01E7->g, U+01E8->k, U+01E9->k, U+01EA->o, U+01EB->o, U+01EC->o, U+01ED->o, U+01F0->j, U+01F4->g, U+01F5->g, U+01F8->n, U+01F9->n, U+01FA->a, U+01FB->a, U+0200->a, U+0201->a, U+0202->a, U+0203->a, U+0204->e, U+0205->e, U+0206->e, U+0207->e, U+0208->i, U+0209->i, U+020A->i, U+020B->i, U+020C->o, U+020D->o, U+020E->o, U+020F->o, U+0210->r, U+0211->r, U+0212->r, U+0213->r, U+0214->u, U+0215->u, U+0216->u, U+0217->u, U+0218->s, U+0219->s, U+021A->t, U+021B->t, U+021E->h, U+021F->h, U+0226->a, U+0227->a, U+0228->e, U+0229->e, U+022A->o, U+022B->o, U+022C->o, U+022D->o, U+022E->o, U+022F->o, U+0230->o, U+0231->o, U+0232->y, U+0233->y, U+1E00->a, U+1E01->a, U+1E02->b, U+1E03->b, U+1E04->b, U+1E05->b, U+1E06->b, U+1E07->b, U+1E08->c, U+1E09->c, U+1E0A->d, U+1E0B->d, U+1E0C->d, U+1E0D->d, U+1E0E->d, U+1E0F->d, U+1E10->d, U+1E11->d, U+1E12->d, U+1E13->d, U+1E14->e, U+1E15->e, U+1E16->e, U+1E17->e, U+1E18->e, U+1E19->e, U+1E1A->e, U+1E1B->e, U+1E1C->e, U+1E1D->e, U+1E1E->f, U+1E1F->f, U+1E20->g, U+1E21->g, U+1E22->h, U+1E23->h, U+1E24->h, U+1E25->h, U+1E26->h, U+1E27->h, U+1E28->h, U+1E29->h, U+1E2A->h, U+1E2B->h, U+1E2C->i, U+1E2D->i, U+1E2E->i, U+1E2F->i, U+1E30->k, U+1E31->k, U+1E32->k, U+1E33->k, U+1E34->k, U+1E35->k, U+1E36->l, U+1E37->l, U+1E38->l, U+1E39->l, U+1E3A->l, U+1E3B->l, U+1E3C->l, U+1E3D->l, U+1E3E->m, U+1E3F->m, U+1E40->m, U+1E41->m, U+1E42->m, U+1E43->m, U+1E44->n, U+1E45->n, U+1E46->n, U+1E47->n, U+1E48->n, U+1E49->n, U+1E4A->n, U+1E4B->n, U+1E4C->o, U+1E4D->o, U+1E4E->o, U+1E4F->o, U+1E50->o, U+1E51->o, U+1E52->o, U+1E53->o, U+1E54->p, U+1E55->p, U+1E56->p, U+1E57->p, U+1E58->r, U+1E59->r, U+1E5A->r, U+1E5B->r, U+1E5C->r, U+1E5D->r, U+1E5E->r, U+1E5F->r, U+1E60->s, U+1E61->s, U+1E62->s, U+1E63->s, U+1E64->s, U+1E65->s, U+1E66->s, U+1E67->s, U+1E68->s, U+1E69->s, U+1E6A->t, U+1E6B->t, U+1E6C->t, U+1E6D->t, U+1E6E->t, U+1E6F->t, U+1E70->t, U+1E71->t, U+1E72->u, U+1E73->u, U+1E74->u, U+1E75->u, U+1E76->u, U+1E77->u, U+1E78->u, U+1E79->u, U+1E7A->u, U+1E7B->u, U+1E7C->v, U+1E7D->v, U+1E7E->v, U+1E7F->v, U+1E80->w, U+1E81->w, U+1E82->w, U+1E83->w, U+1E84->w, U+1E85->w, U+1E86->w, U+1E87->w, U+1E88->w, U+1E89->w, U+1E8A->x, U+1E8B->x, U+1E8C->x, U+1E8D->x, U+1E8E->y, U+1E8F->y, U+1E96->h, U+1E97->t, U+1E98->w, U+1E99->y, U+1EA0->a, U+1EA1->a, U+1EA2->a, U+1EA3->a, U+1EA4->a, U+1EA5->a, U+1EA6->a, U+1EA7->a, U+1EA8->a, U+1EA9->a, U+1EAA->a, U+1EAB->a, U+1EAC->a, U+1EAD->a, U+1EAE->a, U+1EAF->a, U+1EB0->a, U+1EB1->a, U+1EB2->a, U+1EB3->a, U+1EB4->a, U+1EB5->a, U+1EB6->a, U+1EB7->a, U+1EB8->e, U+1EB9->e, U+1EBA->e, U+1EBB->e, U+1EBC->e, U+1EBD->e, U+1EBE->e, U+1EBF->e, U+1EC0->e, U+1EC1->e, U+1EC2->e, U+1EC3->e, U+1EC4->e, U+1EC5->e, U+1EC6->e, U+1EC7->e, U+1EC8->i, U+1EC9->i, U+1ECA->i, U+1ECB->i, U+1ECC->o, U+1ECD->o, U+1ECE->o, U+1ECF->o, U+1ED0->o, U+1ED1->o, U+1ED2->o, U+1ED3->o, U+1ED4->o, U+1ED5->o, U+1ED6->o, U+1ED7->o, U+1ED8->o, U+1ED9->o, U+1EDA->o, U+1EDB->o, U+1EDC->o, U+1EDD->o, U+1EDE->o, U+1EDF->o, U+1EE0->o, U+1EE1->o, U+1EE2->o, U+1EE3->o, U+1EE4->u, U+1EE5->u, U+1EE6->u, U+1EE7->u, U+1EE8->u, U+1EE9->u, U+1EEA->u, U+1EEB->u, U+1EEC->u, U+1EED->u, U+1EEE->u, U+1EEF->u, U+1EF0->u, U+1EF1->u, U+1EF2->y, U+1EF3->y, U+1EF4->y, U+1EF5->y, U+1EF6->y, U+1EF7->y, U+1EF8->y, U+1EF9->y"

Okazuje się jednak że nie jest aż tak różowo – po dodaniu tego kawałka kodu polskie znaki nadal nie były poprawnie rozpoznawane. Sprawdzenie kodu, wyszukiwanie w konsoli i rozmaite testy przyniosły wreszcie powód: “ogonki” były zapisywane nie jako znaki w UTF-8 tylko jako html-owe encje. Jakie więc było moje zdziwienie gdy okazało się że na żadnych stronach opisujących ten problem nie pojawia się również to rozwiązanie. Jeśli też macie problem z brakiem możliwości wyszukiwania polskich znaków to wystarczy tylko dodać do konfiguracji linijkę:

html_strip: 1

I od razu wszystko śmiga – jedyna wada to że Sphinx ignoruje akcenty i zamienia je na znaki ASCII, ale to chyba nie jest zbyt duża cena w zamian za funkcjonalność którą nam daje.

Buty z rubinem

Ostatnimi czasy pracując nad moją pracą inżynierską zacząłem się zastanawiać nad wyborem języka oraz czegoś sensownego do zrobienia interfejsu graficznego. Oczywiście pierwszą rzeczą która przyszła mi na myśl było C++ oraz QT, ale już po chwili stwierdziłem że zamiast iść na łatwiznę czas poznać coś nowego. Jako że aktualnie pracuję z Ruby więc zacząłem się zastanawiać czy nie ma jakiś sensownych bindingów dla tego języka. Poszukałem, znalazłem i stwierdziłem że to nie to. Niby fajne, niby działa, ale jakoś do mnie nie przemawia – za mało “ruby-way”, a do tego mnóstwo zabawy żeby działało u potencjalnego użytkownika(chociaż to już raczej wina samego rubiego niż QT). Poszukałem więc trochę dłużej i znalazłem jeszcze parę alternatyw – mnie lub bardziej pasujących do wymagań(ma być między-systemowe, działać OOTB i mieć jako-taką wydajność). Skończyło się na czymś spełniającym dwa pierwsze założenia i w akceptujący sposób trzecie – a mianowicie na Ruby Shoes.

Po wyborze czas na test – czyli nasze ukochane “hello world”. Szybka instalacja binarki dla OS X i parę linijek kodu później wielkie WOW i szybki test na maszynie wirtualnej pod Win oraz Ubuntu – wszystko działa tak jak miało. Zachwycony biorę się więc do roboty. Muszę przyznać że wszystko działa tak jak trzeba i pod każdym systemem. Do tego dochodzi generowanie pliku wykonywalnego z dołączonym Ruby Shoes więc żadne dodatkowe zależności nie są potrzebne – wszystko działa od razu, sprawnie i bez tricków specyficznych dla danego systemu. Tak więc cudo :)

Schody zaczęły się niestety kiedy potrzebowałem dołączyć do zależności jakieś gemy. Po testach paru wydawało mi się że wszystko będzie ok – w końcu sam instaluje gemy z sieci i jest gites. Problem jest w momencie kiedy potrzebujemy gema z rozszerzeniami natywnymi – tutaj niestety potrzebny jest kompilator, a RubyGems nie jest dostatecznie zaawansowane żeby powiedzieć dokładnie czego potrzebuje. Ale od czego jest readme – umieści się i to. Prawdziwy problem zaczął się jednak przy próbie dołączenia RMagick. Otóż po paru godzinach prób, googlowania i czytania słownie setek możliwych rozwiązań stwierdziłem że jestem w tej wąskiej grupie gdzie nawet najwięksi wyjadacze rozkładają ręce i mówią że pod Snow Leopard nie działa i nie wiadomo dlaczego. Bo w końcu wszystko się kompiluje, uruchamia itd, ale przy próbie wywołania dowolnej funkcji wywala że istniejący bundle nie istnieje i figa. Cóż – z pomocą przyszedł wysłużony VirtualBox i teraz pracuję na 2 ekrany – na jednym OS X z kodem, a na drugim Ubuntu z testami. Można? Można – potem się będę zastanawiał co mogę zrobić żeby działało na wszystkich 3 systemach – może niedługo wyjdzie patch naprawiający tą usterkę? Zobaczymy.

Na zakończenie krótkie podsumowanie. Jeśli potrzebujecie przenośnego frameworka dla aplikacji w Ruby to Shoes jest dla was – działa na każdym systemie, buduje binarki z samym sobą oraz automatycznie ściąga dołączone gemy. Wydajność stoi na przyzwoitym poziomie, łatwość i przyjemność programowania to jakieś 8/10 a i społeczność jest całkiem przyzwoita. Jedyny problem(poza natywnymi rozszerzeniami) to kulejąca trochę dokumentacja – połowy funkcji nie ma w plikach RDoc więc trzeba szukać na własną rękę albo w manualu(co prawda tam powinno się szukać w pierwszej kolejności, ale jak wielu użytkowników Ruby przyzwyczaiłem się do pewnych standardów;) Ale zapewniam że da się to przeboleć i wtedy dość szybko można docenić pracę autora.

Nowy blog

Ostatnimi czasy patrząc na ten swój biedny blog stwierdziłem że troszeczkę tu… smutno? Tak – to chyba dobre określenie. Ale tak to już bywa kiedy bierze się za coś na szybko bo “coś podkusiło” i potem powstaje fuszerka której się nie chce dotykać bo “jeszcze się posypie”. Tak więc odczekałem od świąt i korzystając z nadmiaru czasu pomiędzy jedną sjestą a drugą i przerzuciłem się na Wordpressa. Co prawda w pierwszej chwili byłem trochę przerażony tym ile tu jest opcji, ale jakoś się ogarnąłem i parę rzeczy poprzerabiałem – chyba teraz już będzie wszystko sensownie działać. Nauczka na przyszłość? Jeckyll jest fajnym projektem, ale jeśli ma się ochotę popisać dłużej niż parę postów zanim człowieka krew zaleje to lepiej przejść na coś mniej geekowskiego…