Учебники

Ruby on Rails 2.1 — модульное тестирование

Прежде чем продолжить, давайте кратко рассмотрим несколько определений —

  • Тесты — это тестовые приложения, которые дают согласованный результат и доказывают, что приложение Rails делает то, что от него ожидается. Тесты разрабатываются одновременно с фактическим применением.

  • Утверждение — это одна строка кода, которая оценивает объект (или выражение) для ожидаемых результатов. Например — это значение = это значение? Это объект ноль?

  • Тестовый пример — это класс, унаследованный от Test :: Unit :: TestCase, содержащий стратегию тестирования , состоящую из контекстно-связанных тестов.

  • Набор тестов — это набор тестов. Когда вы запускаете набор тестов, он, в свою очередь, выполняет каждый принадлежащий ему тест.

Тесты — это тестовые приложения, которые дают согласованный результат и доказывают, что приложение Rails делает то, что от него ожидается. Тесты разрабатываются одновременно с фактическим применением.

Утверждение — это одна строка кода, которая оценивает объект (или выражение) для ожидаемых результатов. Например — это значение = это значение? Это объект ноль?

Тестовый пример — это класс, унаследованный от Test :: Unit :: TestCase, содержащий стратегию тестирования , состоящую из контекстно-связанных тестов.

Набор тестов — это набор тестов. Когда вы запускаете набор тестов, он, в свою очередь, выполняет каждый принадлежащий ему тест.

Рельсы Тестирование

Когда вы запускаете скрипт сценария помощника / generate для создания контроллеров и моделей , Rails генерирует среду для модульных и функциональных тестов. Вы можете получить довольно хорошее тестовое покрытие, заполнив фреймворк тестами для написанной вами функциональности. Есть два важных момента для тестирования в приложении Rails:

  • Тестирование моделей

  • Тестирование контроллеров

Тестирование моделей

Тестирование контроллеров

Этот урок кратко охватит оба тестирования. Итак, давайте создадим один testapp, чтобы понять концепцию.

C:\ruby> rails -d mysql testapp

Настройка базы данных

До сих пор мы использовали только базу данных разработки приложения Rails, но теперь вам нужно убедиться, что тестовая база данных также создана и соответствующие разделы вашего файла config / database.yml настроены правильно.

Давайте создадим базы данных для разработки и тестирования следующим образом:

mysql> create database testapp_test;
Query OK, 1 row affected (0.01 sec)

mysql> create database testapp_development;
Query OK, 1 row affected (0.01 sec)

mysql> use testapp_test;
Database changed

mysql> grant all privileges on testapp_test.* 
   to 'root'@'localhost' identified by 'password';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

Конфигурирование database.yml

Настройте ваш config / database.yml следующим образом:

development:
   adapter: mysql
   encoding: utf8
   database: testapp_development
   username: root
   password: password
   host: localhost
test:
   adapter: mysql
   encoding: utf8
   database: testapp_test
   username: root
   password: password
   host: localhost
production:
   adapter: mysql
   encoding: utf8
   database: testapp_production
   username: root
   password: password
   host: localhost

Генерация миграции

Предположим, у вас есть таблица, содержащая книги, включая их названия, цену и небольшое описание. Следующая миграция устанавливает эту таблицу —

testapp > ruby script/generate migration books

Теперь измените файл testapp / db / migrate / 20080616170315_books.rb следующим образом:

class Books < ActiveRecord::Migration
   def self.up
      create_table :books do |t|
         t.string     :title, :limit => 32, :null => false
         t.float      :price
         t.text       :description
         t.timestamp  :created_at
      end
   end
  
   def self.down
      drop_table :books
   end
end

Теперь запустите миграцию следующим образом:

testapp > rake db:migrate

Это создаст таблицу книг в базе данных testapp_development. После этого нам нужно настроить тестовую базу данных с помощью команды rake следующим образом:

C:\ruby\testapp > rake db:test:clone_structure

Это клонирует базу данных testapp_development в базу данных testapp_test . Это означает, что все, что у вас есть в базе данных разработки, теперь у вас будут те же данные в тестовой базе данных.

Модели тестирования

Когда вы генерируете модель с помощью скрипта генерирования, Rails также генерирует скрипт модульного теста для модели в тестовом каталоге. Он также создает прибор , файл YAML, содержащий тестовые данные для загрузки в базу данных testapp_test. Это данные, по которым будут выполняться ваши юнит-тесты —

testapp > ruby script/generate model Book
   exists  app/models/
   exists  test/unit/
   exists  test/fixtures/
   create  app/models/book.rb
   create  test/unit/book_test.rb
   create  test/fixtures/books.yml
   create  db/migrate
   create  db/migrate/20080616164236_create_books.rb

Когда вы пишете код в классах моделей, вы будете писать соответствующие тесты в этих файлах. Итак, давайте создадим две записи тестовой книги с использованием YAML в test / fixtures / books.yml следующим образом:

perl_cb:
   id: 1
   title: 'Ruby Tutorial'
   price: 102.00
   description : 'This is a nice Ruby tutorial'
java_cb:
   id: 2
   title: 'Java Programming'
   price: 62.00
   description : 'Java Programming for the beginners'

Теперь давайте заменим существующий код в файле юнит-теста книги test / unit / book_test.rb следующим кодом —

require File.dirname(__FILE__) + '/../test_helper'

class BookTest < ActiveSupport::TestCase
   fixtures :books

   def test_book

      perl_book = Book.new :title => books(:perl_cb).title, 
         :price => books(:perl_cb).price,
         :description => books(:perl_cb).description,
         :created_at => books(:perl_cb).created_at

      assert perl_book.save

      perl_book_copy = Book.find(perl_book.id)

      assert_equal perl_book.title, perl_book_copy.title

      perl_book.title = "Ruby Tutorial"

      assert perl_book.save
      assert perl_book.destroy
   end
end

Наконец, запустите метод теста следующим образом —

testapp > ruby test/unit/book_test.rb

Вот результат запуска успешного теста —

testapp > ruby test/unit/book_test_crud.rb 
Loaded suite ./test/unit/book_test
Started
.
Finished in 0.0625 seconds.

1 tests, 4 assertions, 0 failures, 0 errors

Давайте проанализируем, что здесь произошло —

  • Метод BookTest начинается с создания нового объекта Book с использованием заголовка и других полей из первой записи в тексте fixture / books.yml. Полученный объект сохраняется в переменной экземпляра perl_book.

  • Первое утверждение проверяет, что сохранение объекта Book прошло успешно.

  • Затем объект book извлекается с использованием метода find и сохраняется в другой переменной экземпляра с именем perl_book_copy. Успешность этого поиска проверяется в следующем утверждении, в котором сравниваются заголовки обоих объектов книги. На данный момент мы проверили способность создавать и читать записи в базе данных.

  • Решение проверяет обновление, назначая новый заголовок объекту, сохраненному в perl_book, и затем подтверждает, что сохранение изменения прошло успешно.

  • Наконец, способность уничтожать объект Book проверяется.

Метод BookTest начинается с создания нового объекта Book с использованием заголовка и других полей из первой записи в тексте fixture / books.yml. Полученный объект сохраняется в переменной экземпляра perl_book.

Первое утверждение проверяет, что сохранение объекта Book прошло успешно.

Затем объект book извлекается с использованием метода find и сохраняется в другой переменной экземпляра с именем perl_book_copy. Успешность этого поиска проверяется в следующем утверждении, в котором сравниваются заголовки обоих объектов книги. На данный момент мы проверили способность создавать и читать записи в базе данных.

Решение проверяет обновление, назначая новый заголовок объекту, сохраненному в perl_book, и затем подтверждает, что сохранение изменения прошло успешно.

Наконец, способность уничтожать объект Book проверяется.

Вот как мы можем протестировать наши модели Rails.

Тестирование контроллеров

Тестирование контроллера также называется функциональным тестированием . Функциональное тестирование тестирует следующие типы функций контроллеров —

  • Ответ перенаправлен как ожидалось?
  • Предполагаемый шаблон отображается?
  • Является ли маршрутизация ожидаемой?
  • Содержит ли ответ ожидаемые теги?

Rails Framework поддерживает пять типов запросов —

  • получить
  • сообщение
  • положил
  • голова
  • удалять

Чтобы написать функциональный тест, вам нужно смоделировать любой из пяти типов HTTP-запросов, которые будет обрабатывать ваш контроллер.

Тип запроса «get» и «post» наиболее часто используются при тестировании контроллера. Все эти методы принимают четыре аргумента —

  • Действие контроллера
  • Необязательный хэш параметров запроса
  • Необязательный хэш сессии
  • Дополнительный флэш-хэш

В этом уроке мы увидим, как использовать метод get для тестирования нашего контроллера. Вы можете проверить остальные методы аналогичным образом.

Когда вы генерируете контроллер с помощью generate, Rails создает скрипт функционального теста для контроллера следующим образом:

testapp > ruby script/generate controller Book
   exists  app/controllers/
   exists  app/helpers/
   create  app/views/book
   exists  test/functional/
   create  app/controllers/book_controller.rb
   create  test/functional/book_controller_test.rb
   create  app/helpers/book_helper.rb

Когда вы пишете код в классах контроллера, вы будете писать соответствующие тесты в этих файлах. Перед этим давайте определим наш список функций контроллера , покажем и ищем внутри app / controllers / book_controller.rb следующим образом:

class BookController < ApplicationController
   def list
      @book_pages, @books = paginate :books, :per_page => 10
   end

   def show
      @book = Book.find(params[:id])
   end

   def search
      @book = Book.find_by_title(params[:title])
      if @book
         redirect_to :action => 'show', :id => @book.id
      else    
         flash[:error] = 'No such book available'
         redirect_to :action => 'list'
      end
   end
end

ПРИМЕЧАНИЕ. — Вам потребуется два шаблона представлений для метода показа и списка . Вы можете определить эти представления и протестировать их, но сейчас мы продолжим без определения этих представлений.

Теперь давайте повторно используем наш тестовый прибор, который находится в файле test / fixtures / books.yml, следующим образом:

perl_cb:
   id: 1
   title: 'Ruby Tutorial'
   price: 102.00
   description : 'This is a nice Ruby tutorial'
java_cb:
  id: 2
  title: 'Java Programming'
  price: 62.00
  description : 'Java Programming for the beginners'

Добавьте следующие методы test_search_book и test_search_not_found в test / functions / book_controller_test.rb, чтобы проверить функциональность поискового действия контроллера книги.

require File.dirname(__FILE__) + '/../test_helper'
require 'book_controller'

# Re-raise errors caught by the controller.
class BookController
   def rescue_action(e) 
      raise e 
   end
end

class BookControllerTest < Test::Unit::TestCase
   fixtures :books
   def setup
      @controller = BookController.new
      @request    = ActionController::TestRequest.new
      @response   = ActionController::TestResponse.new
   end

   def test_search_book
      get :search, :title => 'Ruby Tutorial'
      assert_not_nil assigns(:book)
      assert_equal books(:perl_cb).title, assigns(:book).title
      assert_valid assigns(:book)
      assert_redirected_to :action => 'show'
   end

   def test_search_not_found
      get :search, :title => 'HTML Tutorial'
      assert_redirected_to :action => 'list'
      assert_equal 'No such book available', flash[:error]
   end
end

Теперь запустите ваши тесты следующим образом:

testapp > ruby test/functional/book_controller_test.rb 

Это дает следующий вывод —

Loaded suite test/functional/book_controller_test
Started
..
Finished in 0.422 seconds.

2 tests, 7 assertions, 0 failures, 0 errors

Давайте проанализируем, что здесь произошло —

  • Метод настройки является методом по умолчанию для создания объектов контроллера, запроса и ответа. Они будут использоваться Rails внутри страны.

  • Первый тестовый метод test_search_book генерирует запрос get для действия поиска, передавая параметр title .

  • Следующие два утверждения подтверждают, что объект Book был сохранен в переменной экземпляра с именем @book и что объект проходит любые проверки Active Record, которые могут существовать.

  • Последнее утверждение в первом методе проверяет, что запрос был перенаправлен в действие show контроллера.

  • Второй метод test, test_search_not_found , выполняет еще один запрос get, но передает неверный заголовок

  • Первое утверждение проверяет, что было выполнено перенаправление на действие списка .

  • Если исходящие утверждения пройдены, во флэш- хэше должно быть сообщение, которое вы можете проверить с помощью assert_equal.

Метод настройки является методом по умолчанию для создания объектов контроллера, запроса и ответа. Они будут использоваться Rails внутри страны.

Первый тестовый метод test_search_book генерирует запрос get для действия поиска, передавая параметр title .

Следующие два утверждения подтверждают, что объект Book был сохранен в переменной экземпляра с именем @book и что объект проходит любые проверки Active Record, которые могут существовать.

Последнее утверждение в первом методе проверяет, что запрос был перенаправлен в действие show контроллера.

Второй метод test, test_search_not_found , выполняет еще один запрос get, но передает неверный заголовок

Первое утверждение проверяет, что было выполнено перенаправление на действие списка .

Если исходящие утверждения пройдены, во флэш- хэше должно быть сообщение, которое вы можете проверить с помощью assert_equal.

Чтобы получить больше информации об утверждениях, обратитесь к стандартной документации Rails .

Использование Rake для тестирования

Вы можете использовать утилиту rake для тестирования ваших приложений. Ниже приведен список нескольких важных команд.

$ rake test — тестирует все модульные и функциональные тесты (и интеграционные тесты, если они существуют).

$ rake test: functionals — Запустить все функциональные тесты.

$ rake test: units — Запустить все модульные тесты.

$ rake test: интеграция — запустить все интеграционные тесты.

$ rake test: plugins — Запустите все тесты в ./vendor/plugins/**/test.

$ rake test: недавно — запустить тесты для моделей и контроллеров, которые были изменены за последние 10 минут —

$ rake test: uncommitted — для проектов в Subversion запустите тесты на изменения, которые произошли в моделях и контроллерах с момента последней фиксации —