Автотесты Netzke в браузере
Создание начальной оболочки для тестирования формы входа. Написана с высоты двухнедельного изучения ruby и Netzke. Сдалано топорно.

По мотивам
- Testing Ext JS/Rails components with Cucumber and WebDriver in Netzke
- Rails: Хватит отмазываться, начинаем BDD-ить!
Весь код примера доступен на GitHub
Создаем новый проект
$ rails new netzke-test-1
create
create README
create Rakefile
create config.ru
create .gitignore
...
$ cd netzke-test-1
Добавляем gems в файл Gemfile:
source 'http://rubygems.org' gem 'rails', '3.0.3' gem 'sqlite3-ruby', :require => 'sqlite3' gem 'netzke-core', :git => "git://github.com/skozlov/netzke-core.git" gem 'netzke-basepack', :git => "git://github.com/skozlov/netzke-basepack.git" gem 'will_paginate' group :development, :test do gem "cucumber-rails" gem "rspec-rails" end group :test do gem "capybara" gem "launchy" gem "database_cleaner" gem "selenium-webdriver" end
… и устанавливаем их:
$ bundle install Updating git://github.com/skozlov/netzke-core.git Updating git://github.com/skozlov/netzke-basepack.git Fetching source index for http://rubygems.org/ Using rake (0.8.7) Using abstract (1.0.0) Using activesupport (3.0.3) Using builder (2.1.2) ...
Делаем линки на скаченные библиотеки ExtJS и иконки:
$ ln -s ~/Library/mylibs/ext-3.3.1 public/extjs $ ln -s ~/Library/mylibs/famfamfam_silk_icons_v013/icons public/images/
Добавляем в /app/views/layout/application.rb
<head> <title>NetzkeTest1</title> <%= netzke_init %> <%= csrf_meta_tag %> </head>
Генерим оболочку:
$ rails g rspec:install $ rails g cucumber:install --rspec --capybara
Делаем чтобы тесты выполнялись красиво:
$ echo '--colour --format documentation' > .rspec $ sed -i~ 's/progress/pretty/g' config/cucumber.yml
Прописываем драйвер в тесты, для этого вписываем в features/support/env.rb следующие строки:
Capybara.register_driver :selenium do |app| Capybara::Driver::Selenium.new app, :browser => :chrome end
Пишем фичу в файле features/login.feature
1 Feature: Check login window 2 In order to value 3 As a role 4 I want feature 5 6 @javascript 7 Scenario: Check window properties (and button Enter should be disabled by default) 8 Given I am on the login page 9 Then I should see "Login window" 10 And I should see "User" 11 And I should see "Password" 12 Then button "Enter" should be disabled 13 When I fill in "username" with "some" 14 And I fill in "password" with "somepass" 15 And I sleep 1 seconds 16 Then button "Enter" should be enabled 17 18 @javascript 19 Scenario: Check login 20 Given I have a user named "user1" with password "pass1" 21 And I am on the login page 22 When I fill in "username" with "wrong_user" 23 And I fill in "password" with "wrong_password" 24 And I press "Enter" 25 When I wait for the response from the server 26 And I sleep 1 seconds 27 Then I should see "Wrong!" 28 When I fill in "username" with "user1" 29 And I fill in "password" with "pass1" 30 And I press "Enter" 31 When I wait for the response from the server 32 And I sleep 1 seconds 33 Then I should see "Hello, user1!"
Что мы делаем:
В первом сценарии мы проверяем окно, которое должно появится, два поля и отключенная кнопка “Enter”, которая должна включится только если в первом и во втором поле что-либо есть.
И второй сценарий – при верных параметрах мы попадаем на страницу приветствия пользователя, а при неверных – сообщение об ошибке.
PS: Тест написано криво и его надо причесать, но для примера пойдет.
PPS: Понаставил пауз потому как не знаю как же дожидаться выполнения работы JS.
Запускаем тест cucumber features и видим кучу ошибок. Самая первая из них:
Can't find mapping from "the login page" to a path.
Генерируем контроллер и добавляем action index
$ rails g controller login index
create app/controllers/login_controller.rb
invoke erb
create app/views/login
...
Добавляем default route к нашей странице в файле config/routes.rb
netzke match "login" => "login#index"
Запускаем наш тест и видим, что он запнулся на том, что не видит окно.
Then I should see "Login window"
Это нормально, потому что мы его еще не написали.
Теперь рисуем наше окно для входа в файле app/views/login/index.html.erb
<%= netzke :login %>
И сам компонент. Для этого создаем католог app/components и файл app/components/login.rb:
1 # coding: utf-8 2 class Login < Netzke::Base 3 4 js_base_class 'Ext.Window' 5 6 def configuration 7 super.merge( 8 :title => 'Login window', 9 :hidden => false, 10 :width => 350, :autoHeight => true, 11 :closable => false, :resizable => false, 12 :items => [{ 13 :buttons => [ :login.action ], 14 :xtype => :form, 15 :frame => true, 16 :defaultType => :textfield, 17 :monitorValid => true, 18 :defaults => { :anchor => '100%', :allowBlank => false }, 19 :items => [{ 20 :name => 'username', 21 :fieldLabel => 'User' 22 },{ 23 :name => 'password', 24 :fieldLabel => 'Password', 25 :inputType => :password 26 }] 27 }] 28 ) 29 end 30 31 action :login, :text => 'Enter', :icon => :accept, :formBind => true 32 33 js_method :on_login, <<-JS.l 34 function(){ 35 var form = this.items.first().getForm(); 36 if (form.isValid()){ 37 this.login(form.getFieldValues()); 38 } 39 } 40 JS 41 42 endpoint :login do |para| 43 user = User.find(:first, :conditions => { 44 :login => para['username'], 45 :password => para['password'] 46 }) 47 session[:user_id] = user.id unless user.nil? 48 { :get_answer => !user.nil? } 49 end 50 51 js_method :get_answer, <<-JS.l 52 function (ret){ 53 if (ret == false) { 54 Ext.Msg.alert('Wrong!','Wrong username or password'); 55 }else{ 56 window.location = '/users'; 57 } 58 } 59 JS 60 end
Строки 6-29: Описание окна.
Строка 31: Добавление action типа “кнопка” и привязывание к форме
Строки 33-40: Отправка формы на сервер
Строки 42-50: Проверка пароля на строне сервера (так делать нельзя – это только пример)
Строки 52-60: Приемка ответа от сервера и решения что же дальше
Теперь cucumber ругается на непонятный ему шаг:
Undefined step: "button "Enter" should be disabled" (Cucumber::Undefined)
Отлично! Так давайте его и напишем в файле features/step_definitions/my_netzke_steps.rb:
Then /^button "([^"]*)" should be enabled$/ do |arg1| page.driver.browser.execute_script(<<-JS).should == true var btn = Ext.ComponentMgr.all.filter('text', '#{arg1}').filter('type','button').first(); return typeof(btn)!='undefined' ? !btn.disabled : false JS end Then /^button "([^"]*)" should be disabled$/ do |arg1| page.driver.browser.execute_script(<<-JS).should == true var btn = Ext.ComponentMgr.all.filter('text', '#{arg1}').filter('type','button').first(); return typeof(btn)!='undefined' ? btn.disabled : false JS end
У нас готов первый полностью пройденный сценарий, на котором мы проверям окно входа и неактивную кнопку. Идем дальше.
Undefined step: "I have a user named "user1" with password "pass1""
Система не знает как сделать пользователя – так подскажем как это делается. Добавляем описание этого шага в features/step_definitions/my_netzke_steps.rb
Given /^I have a user named "([^"]*)" with password "([^"]*)"$/ do |arg1, arg2| User.create!( :login => arg1, :password => arg2 ) end
OMG! У нас нет модели! Cucumber говорит что uninitialized constant User (NameError). Создадим:
$ rails g model User login:string password:string
invoke active_record
create db/migrate/20110214075142_create_users.rb
create app/models/user.rb
invoke rspec
create spec/models/user_spec.rb
$ rake db:migrate
$ rake db:test:prepare
...
Осталось два последних шага, которых не понимает cucumber: When I wait for the response from the server и I sleep (\d+) seconds. Напишем их в features/step_definitions/my_netzke_steps.rb
When /^I wait for the response from the server$/ do page.wait_until{ page.driver.browser.execute_script("return !Ext.Ajax.isLoading();") } end When /I sleep (\d+) seconds?/ do |arg1| sleep arg1.to_i end
И наконец-то cucumber наткнулся Then I should see “Hello, user1!” на нашу последнюю недописанную часть – то, что должно появится после входа в систему. Для этого создадим котроллер users, в котором приветствуем пользователя:
$ rails g controller users index
И default route config/routes.rb
match "users" => "users#index"
И напишем в app/views/users/index.html.erb:
Hello, <%= User.find(session[:user_id]).login %>!
Всё. Тесты пройдены – вход работет.