bbq-core/README.md

Summary

Maintainability
Test Coverage
# BBQ

[![Build Status](https://secure.travis-ci.org/drugpl/bbq-core.png)](http://travis-ci.org/drugpl/bbq-core) [![Dependency Status](https://gemnasium.com/drugpl/bbq-core.png)](https://gemnasium.com/drugpl/bbq-core) [![Code Climate](https://codeclimate.com/github/drugpl/bbq-core.png)](https://codeclimate.com/github/drugpl/bbq-core) [![Gem Version](https://badge.fury.io/rb/bbq-core.png)](http://badge.fury.io/rb/bbq-core)

Object oriented acceptance testing using personas.

* Ruby (no Gherkin)
* Objects and methods instead of steps
* Test framework independent (RSpec and Test::Unit support)
* Thins based on Capybara.
* DCI (Data Context Interaction) for roles/personas
* Opinionated

## Setup

First, add BBQ to your apps `Gemfile`:

```ruby
gem "bbq", "0.2.1"
```

Run install generator:

```
bundle exec rails generate bbq:install
```

Require BBQ in test/test_helper.rb (in case of Test::Unit):

```ruby
require "bbq/test_unit"
```

Require BBQ in spec/spec_helper.rb (in case of RSpec):

```ruby
require "bbq/rspec"
```

## Feature generator

```
bundle exec rails g bbq:test MyFeatureName
```

## Running features

For Test::Unit flavour:

```
bundle exec rake test:acceptance
```

For RSpec flavour:

```
bundle exec rake spec:acceptance
```

## Examples

### Roles and Devise integration

```ruby
class TestUser < Bbq::TestUser
  include Bbq::Devise

  def update_ticket(summary, comment)
    show_ticket(summary)
    fill_in  "Comment", :with => comment
    click_on "Add update"
  end

  def open_application
    visit '/'
  end

  module TicketReporter
    def open_tickets_listing
      open_application
      click_link 'Tickets'
    end

    def open_ticket(summary, description)
      open_tickets_listing
      click_on "Open a new ticket"
      fill_in  "Summary", :with => summary
      fill_in  "Description", :with => description
      click_on "Open ticket"
    end

    def show_ticket(summary)
      open_tickets_listing
      click_on summary
    end
  end

  module TicketManager
    def open_administration
      visit '/admin'
    end

    def open_tickets_listing
      open_administration
      click_link 'Tickets'
    end

    def close_ticket(summary, comment = nil)
      open_tickets_listing
      click_on summary
      fill_in  "Comment", :with => comment if comment
      click_on "Close ticket"
    end

    def show_ticket(summary)
      open_tickets_listing
      click_on summary
    end
  end
end
```

```ruby
class AdminTicketsTest < Bbq::TestCase
  background do
    admin = Factory(:admin)
    @email, @password = admin.email, admin.password
  end

  scenario "admin can browse all user tickets" do
    summaries    = ["Forgot my password", "Page is not displayed correctly"]
    descriptions = ["I lost my yellow note with password under the table!",
                    "My IE renders crap instead of crispy fonts!"]

    alice = TestUser.new
    alice.roles(:ticket_reporter)
    alice.register_and_login
    alice.open_ticket(summaries.first, descriptions.first)

    bob = TestUser.new
    bob.roles(:ticket_reporter)
    bob.register_and_login
    bob.open_ticket(summaries.second, descriptions.second)

    charlie = TestUser.new(:email => @email, :password => @password)
    charlie.login # charlie was already "registered" in factory as admin
    charlie.roles(:ticket_manager)
    charlie.open_tickets_listing
    charlie.see!(*summaries)

    charlie.click_on(summaries.second)
    charlie.see!(summaries.second, descriptions.second)
    charlie.not_see!(summaries.first, descriptions.first)
  end
end
```

### RSpec integration

```ruby
class TestUser < Bbq::TestUser
  def email
    @options[:email] || "buyer@example.com"
  end

  module Buyer
    def ask_question(question)
      fill_in "question", :with => question
      fill_in "email", :with => email
      click_on("Ask")
    end

    def go_to_page_and_open_widget(page_url, &block)
      go_to_page(page_url)
      open_widget &block
    end

    def go_to_page(page_url)
      visit page_url
      wait_until { page.find("iframe") }
    end

    def open_widget
      within_widget do
        page.find("#widget h3").click
        yield if block_given?
      end
    end

    ef within_widget(&block)
      within_frame(widget_frame, &block)
    end

    def widget_frame
      page.evaluate_script("document.getElementsByTagName('iframe')[0].id")
    end
  end
end
```

```ruby
feature "ask question widget" do
  let(:user) {
    user = TestUser.new(:driver => :webkit)
    user.roles('buyer')
    user
  }

  scenario "as a guest user, I should be able to ask a question" do
    user.go_to_page_and_open_widget("/widget") do
      user.ask_question "my question"
      user.see!("Thanks!")
    end
  end
end
```

## Testing REST APIs

Bbq provides `Bbq::TestClient`, similar to `Bbq::TestUser`, but intended for testing APIs.
It's a thin wrapper around `Rack::Test` which allows you to send requests and run assertions
against responses.

```ruby
class ApiTest < Bbq::TestCase
  background do
    headers = {'HTTP_ACCEPT' => 'application/json'}
    @client = TestClient.new(:headers => headers)
  end

  scenario "admin can browse all user tickets" do
    @client.get "/unicorn" do |response|
      assert_equal 200, response.status
      assert_equal "pink", response.body["unicorn"]["color"]
    end
    @client.post "/ponies", { :name => "Miracle" } do |response|
      assert_equal 200, response.status
    end
  end
end
```

## Rails URL Helpers

Using url helpers from Rails in integration tests is not recommended.
Testing routes is part of integration test, so you should actually use only

```ruby
  visit '/'
```

in your integration test. Use links and buttons in order to get to other pages in your app.

If you really need url helpers in your test user, just include them in your TestUser class:

```ruby
require 'bbq/rails/routes'

class TestUser < Bbq::TestUser
  include Bbq::Rails::Routes
end
```
or just

```ruby
class TestUser < Bbq::TestUser
  include ::ActionDispatch::Routing::UrlFor
  include ::Rails.application.routes.url_helpers
  include ::ActionDispatch::Routing::RouteSet::MountedHelpers unless ::Rails.version < "3.1"
end
```

## Devise support

```ruby
require "bbq/test_user"
require "bbq/devise"

class TestUser < Bbq::TestUser
  include Bbq::Devise
end
```

After that TestUser have *login*, *logout*, *register*, *register_and_login* methods.

```ruby
test "user register with devise" do
  user = TestUser.new # or TestUser.new(:email => "email@example.com", :password => "secret")
  user.register_and_login
  user.see!("Stuff after auth")
end
```

## Caveats

### Timeout::Error

If you simulate multiple users in your tests and spawn multiple browsers with selenium it might
be a good idea to use Thin instead of Webrick to create application server.
We have experienced some problems with Webrick that lead to `Timeout::Error` exception
when user/browser that was inactive for some time (due to other users/browsers
activities) was requested to execute an action.

Capybara will use Thin instead of Webrick when it's available, so you only need to add Thin to you Gemfile:

```ruby
# In test group if you want it to
# be used only in tests and not in your development mode
# ex. when running 'rails s'

gem 'thin', :require => false
```

## Additional information

* [2 problems with Cucumber](http://andrzejonsoftware.blogspot.com/2011/03/2-problems-with-cucumber.html)
* [Object oriented acceptance testing](http://andrzejonsoftware.blogspot.com/2011/04/object-oriented-acceptance-testing.html)