Cucumber: An Introduction for Non-Rubyists – Setup and the Basics

Out there in the wild, there are a lot of good acceptance test tools and BDD frameworks – and we already had numerous blog posts here at the codecentric blog about the Robot Framework, Fitnesse, JBehave, Geb and others. One big player in this area is still missing: Cucumber. So, it’s time to fill the gap. This post is an introduction to Cucumber, a BDD framework that can be used to write acceptance tests for web applications which are very readable, maintainable and elegant.

This is part one of a two part tutorial on Cucumber. It goes through the setup of Cucumber and introduces the basics of Cucumber and Gherkin. If you are already familiar with Cucumber you can directly skip ahead to part two, which introduces Capybara, Poltergeist and PhantomJS and shows how to use this stack to write acceptance tests for web applications.

Cucumber has an interesting family tree, which goes a little like this: JBehave was ported to Ruby under the name RBehave, which was rewritten from scratch and renamed to RSpec, to which an RSpec Story runner was added, which evolved into Cucumber. So, historically Cucumber is a remote descendant of JBehave. To complete the circle, Cucumber (originally implemented in Ruby) has now been ported back to the JVM with Cucumber JVM. If you are looking for a JVM based BDD framework, both JBehave and Cucumber JVM should be on your list for evaluation. There is a quick and somewhat superficial comparison on Stack Overflow, in case you’re interested.

Cucumber has been ported to a large number of other platforms, too, so nowadays you can use Cucumber with Ruby (the original), Java, .NET, Adobe Flex, Python, Perl, Erlang, PHP, you name it. You could even use the same Gherkin features (see below) on different platforms, although that is probably not a realistic option because the step implementations are platform specific.

Cucumber, as many other BDD frameworks, is rarely used in isolation. In particular, if you want to do any serious web application testing, you’ll need a full stack consisting (at least) of a browser driver and a browser to execute the tests. In most cases you will also not want to use the browser driver directly, but instead use an abstraction layer on top of that, which makes the test code much more readable and maintainable.

Even if you are experienced in writing automated acceptance tests, the options and possible combinations are intimidatingly numerous. On the Ruby platform alone you could combine Cucumber with

  • Selenium WebDriver (via the selenium-webdriver gem),
  • the Watir WebDriver (a layer on top of Selenium WebDriver),
  • Capybara and Webrat,
  • Capybara and Selenium WebDriver or
  • Capybara and Poltergeist (a driver for PhantomJS)

…to just name a few. Additionally, most of these drivers can drive multiple browser, so you need to decide if you want to do headless testing with PhantomJS or Steam or use a “real” browser like Firefox, Chrome or IE for your testing. The bottom line is, if you are evaluating Cucumber for use in your project, you will also need to evaluate the complete stack (or even multiple stacks) which doesn’t make things easier.

This tutorial uses Cucumber on Ruby. In the second part we add Capybara, Poltergeist and PhantomJS to the mix. In my experience, this stack works very well, especially for apps that make heavy use of AJAX.

Never mind if you’re not a rubyist (I don’t consider myself one, either). The target audience for this tutorial are people with no or only little ruby experience. It takes you through the installation and configuration of Cucumber, shows you how to write your first Cucumber feature and how to use Cucumber and Capybara to write beautiful acceptance tests for a web application. The examples should be quite readable without prior ruby knowlegde and it should be possible to translate the code to a different stack.

Setting up Ruby, Cucumber and PhantomJS

Clone the example project (which contains the Gemfile you will need during the setup) by doing

git clone -b 00_setup https://github.com/basti1302/audiobook-collection-manager-acceptance.git

Then, to set up Cucumber and PhantomJS on your system, follow one of the following Gists. We do not need PhantomJS for this part of the tutorial, so you can leave that for later when you start with part two.

When you have completed the setup, typing cucumber in the top level directory in the example project should give you the following output:

0 scenarios
0 steps
0m0.000s

Note: Since we are using bundler, we should actually type bundle exec cucumber instead of simply typing cucumber. This ensures that the cucumber command is executed in the context of the bundle defined in the Gemfile. As long as you do not have multiple versions of ruby gems installed on your system, there should be no difference. But if Cucumber behaves unexpectedly, try your luck with bundle exec cucumber – or train yourself to always use bundle exec cucumber right from the start, because it is a good habit anyway.

Cucumber Basics

Features and Scenarios

The top most artifacts of Cucumber are called features. They are defined in a DSL called Gherkin that more or less resembles natural language. (How well the features resemble natural language of course depends on the author of the feature.) Feature files have the suffix .feature and live in a directory named features. When the Cucumber executable is started without arguments, it always looks for a subdirectory of the current working directory that has this name – so it’s a good idea to start Cucumber in the parent directory of features, otherwise it will complain.

A feature can contain a description (which is unparsed text) and one or more scenarios (think scenario ~ test). A scenario describes one behavioral aspect of the system under test. It makes assertions that – given that certain preconditions hold – the system behaves or responds in an expected way when a specified action is executed.

A scenario is comprised of steps. Each step is a short sentence that falls into one of the three familiar BDD categories:

  • Given (setting up a precondition for the scenario),
  • When (execute an action in the application under test) and
  • Then (assert the desired outcome)

On a technical level, it does not matter if you prefix a step with Given, When, Then or And (which can also be used for all three categories). You could set up preconditions in a Then step and make assertions in a Given step – but using the conventions correctly makes scenarios much more readable.

Further down the chain, there needs to be a step definition for each step. These live in the directory features/step_definitions. We’ll take a closer look at them later.

Your First Feature

If you want to follow along, put the following code into a file named features/first.feature. Instead of copy/pasting it yourself, you can also use the prepared branch: git checkout 01_first_feature.

Note: All code shown here is contained in the example repository. The repository has several branches that loosely represent the progress of the tutorial. You can get a list of available branches with git branch -r. The branch names are mentioned throughout the text of this tutorial, whenever they are relevant. If you have modified the code in a branch and git complains about your uncommitted changes when you try to do git checkout branchname, you can either throw away your edits with git reset --hard or commit them by doing git add -A, followed by git commit -am"my modifications".

#encoding: utf-8
Feature: Showcase the simplest possible Cucumber scenario
  In order to verify that cucumber is installed and configured correctly
  As an aspiring BDD fanatic 
  I should be able to run this scenario and see that the steps pass (green like a cuke)
 
  Scenario: Cutting vegetables
    Given a cucumber that is 30 cm long
    When I cut it in halves
    Then I have two cucumbers
    And both are 15 cm long

Now run the feature. Go to the root directory of the project (the directory directly above features) and type cucumber (or better: bundle exec cucumber). The output should be something like this:

#encoding: utf-8
Feature: Showcase the simplest possible cucumber scenario
  In order to verify that cucumber is installed and configured correctly
  As an aspiring BDD fanatic 
  I should be able to run this scenario and see that the steps pass (green like a cuke)

  Scenario: Cutting vegetables          # features/first.feature:8
    Given a cucumber that is 30 cm long # features/first.feature:9
    When I cut it in halves             # features/first.feature:10
    Then I have two cucumbers           # features/first.feature:11
    And both are 15 cm long             # features/first.feature:12

1 scenario (1 undefined)
4 steps (4 undefined)
0m0.003s

You can implement step definitions for undefined steps with these snippets:

Given(/^a cucumber that is (d+) cm long$/) do |arg1|
  pending # express the regexp above with the code you wish you had
end

When(/^I cut it in halves$/) do
  pending # express the regexp above with the code you wish you had
end

Then(/^I have two cucumbers$/) do
  pending # express the regexp above with the code you wish you had
end

Then(/^both are (d+) cm long$/) do |arg1|
  pending # express the regexp above with the code you wish you had
end

If you want snippets in a different programming language,
just make sure a file with the appropriate file extension
exists where cucumber looks for step definitions.

After echoing the feature the result is listed. It complains that we did not yet define the steps that we have used in this feature. Quite true. But Cucumber goes a step further – it even says how we can solve this and implement the steps (as stubs). How exceptionally nice and polite. It also guesses (correctly) that the numbers used in the steps might vary and replaces the literal numbers with capturing groups. So lets just paste Cucumber’s suggestions into a step definition file. (In fact I often run Cucumber intentionally with undefined steps and use the suggestions as a template for the step implementations.)

Matching Steps With Step Definitions

Create the directory features/step_definitions and in this directory the file first_steps.rb. Yes, step definitions are written in ruby – at least when we are using plain vanilla Cucumber. As mentioned before, there are Cucumber ports for a multitude of languages, so chances are that you can write the step definitions in the language you are most comfortable with. However, this blog post uses the original and so we are going to do some ruby now. Don’t be afraid, step definitions don’t need to be overly complicated and even if you are completely new to ruby you’ll be able to learn enough of it to write step definitions as we go along.

The following code will go into features/step_definitions/first_steps.rb (the name does not matter, all .rb files in features/step_definitions will be loaded when running the features).

#encoding: utf-8
Given /^a cucumber that is (\d+) cm long$/ do |arg1|
  pending # express the regexp above with the code you wish you had
end
 
When /^I cut it in havles$/ do
  pending # express the regexp above with the code you wish you had
end
 
Then /^I have two Cucumbers$/ do
  pending # express the regexp above with the code you wish you had
end
 
Then /^both are (\d+) cm long$/ do |arg1|
  pending # express the regexp above with the code you wish you had
end

Now just run Cucumber again (remember, from the parent directory of features). The output only looks a little bit better:

#encoding: utf-8
Feature: Showcase the simplest possible cucumber scenario
  In order to verify that cucumber is installed and configured correctly
  As an aspiring BDD fanatic 
  I should be able to run this scenario and see that the steps pass (green like a cuke)

  Scenario: Cutting vegetables          # features/first.feature:8
    Given a cucumber that is 30 cm long # features/step_definitions/first_steps.rb:1
      TODO (Cucumber::Pending)
      ./features/step_definitions/first_steps.rb:2:in `/^a cucumber that is (d+) cm long$/'
      features/first.feature:9:in `Given a cucumber that is 30 cm long'
    When I cut it in halves             # features/step_definitions/first_steps.rb:5
    Then I have two cucumbers           # features/step_definitions/first_steps.rb:9
    And both are 15 cm long             # features/step_definitions/first_steps.rb:13

1 scenario (1 pending)
4 steps (3 skipped, 1 pending)
0m0.005s

The summary tells us that at least one step is pending and so the whole scenario is marked as pending. A scenario is skipped entirely when the first step fails (or is pending), so Cucumber does not even try to execute the other pending steps – that’s why it reports 3 steps as skipped.

Before we implement the steps, let’s take a close look on first_steps.rb. If you have never seen a Cucumber step file before, it might look a little weird. The individual step implementations look a little bit like methods – in fact, they are ruby methods and the code inside is just plain ruby code. But the method header looks a bit queer – it contains a regular expression. That is a very neat feature: When Cucumber needs to find the step definition for a step in a scenario, it checks all step definition files (all .rb files in features/step_definitions, including subdirectories). If it finds one with a matching regular expression, this implementation is called. (By the way, if it finds more than one match, it complains about an ambiguous match, instead of executing the first or an arbitrary step.)

This mechanism has several advantages. The first is flexible and well-readable parameterization of step definitions. We have already seen that in

Given /^a cucumber that is (d+) cm long$/ do |arg1|

Here the match of the first capturing group (enclosed in parentheses) is passed into the step implementation as the first argument (called arg1 – we should probably rename it to length). The d+ matches one or more digits, so it is clear that we need to pass a number here.

The regular expression matching also gives you the ability to write scenarios that are nice to read, without duplication in the step file. Here’s a simple example on how to exploit this: We could rewrite the second step as

When /^I (?:cut|chop) (?:it|the cucumber) in (?:halves|half|two)$/ do
  pending # express the regexp above with the code you wish you had
end

With this change all of the following steps would match:

When I cut the cucumber in halves
When I chop the cucumber in half
When I cut it in two

(With regard to vegetable cutting, “cutting in halves” is better english than “cutting in half”, but that’s not the point here.)

We have used non-capturing groups (starting with "(?:") to cater for expressive variety. This is quite common in Cucumber steps.

Once you start using this pattern, you should be careful to not take it too far. Only provide for the step variations that you really need right now. In the end you do not want to spend more time fighting with regular expressions than testing your system. It might even make sense to write two step definitions if it is too difficult to put all variations into one regex. You also have two other options at your disposal to reduce duplication: First, you can put plain ruby methods into the step file and call them from your steps or you can require other ruby files and reuse their code. Finally, you can even call other steps from within a step.

Implementing Steps

Now for what’s inside a step implementation: Currently, all our steps only contain a call to pending. This is like a todo marker. With pending, you can first write you scenario, stub all steps with pending and then implement the steps one after another until the scenario passes. This fits nicely into an outside-in ATDD approach where you drive the implementation of the system under test with acceptance tests:

  1. Write a Cucumber feature first (with pending steps),
  2. implement the first step in Cucumber,
  3. implement the required funcionality in the system under test to make just this step pass,
  4. repeat.

All this said, let’s put some real implementation into the step methods. The code is also available per git checkout 02_step_definitions.

Given /^a cucumber that is (\d+) cm long$/ do |length|
  @cucumber = {:color => 'green', :length => length.to_i}
end
 
When /^I (?:cut|chop) (?:it|the cucumber) in (?:halves|half|two)$/ do
  @choppedCucumbers = [
    {:color => @cucumber[:color], :length => @cucumber[:length] / 2},
    {:color => @cucumber[:color], :length => @cucumber[:length] / 2}
  ]
end
 
Then /^I have two cucumbers$/ do
  @choppedCucumbers.length.should == 2
end
 
Then /^both are (\d+) cm long$/ do |length|
  @choppedCucumbers.each do |cuke|
    cuke[:length].should == length.to_i
  end
end

If you run Cucumber again, you should have the following output:

#encoding: utf-8
Feature: Showcase the simplest possible cucumber scenario
  In order to verify that cucumber is installed and configured correctly
  As an aspiring BDD fanatic 
  I should be able to run this scenario and see that the steps pass (green like a cuke)

  Scenario: Cutting vegetables          # features/first.feature:8
    Given a cucumber that is 30 cm long # features/step_definitions/first_steps.rb:3
    When I cut it in halves             # features/step_definitions/first_steps.rb:7
    Then I have two cucumbers           # features/step_definitions/first_steps.rb:14
    And both are 15 cm long             # features/step_definitions/first_steps.rb:18

1 scenario (1 passed)
4 steps (4 passed)
0m0.005s

Yay! Now all steps are printed in green – like a cucumber (that’s why the tool has been named after the fruit).

Let’s review the implementation: In ruby, an identifier that begins with @ is an instance variable. You do not need to declare them up front, if you assign a value to it at runtime, the instance variable is defined on the fly. That’s why @cucumber is visible in all steps and can be used to keep some state across step boundaries. The expressions enclosed in curly braces are hashes (aka dictionaries or associative arrays, think java.util.Map if you’re coming from the JVM) and the identifiers beginning with a colon are the keys (the colon makes them a symbol, but you don’t need to worry about that for now).

The expressions @choppedCucumbers.length.should == 2 and cuke[:length].should == length.to_i might require a bit of explanation. What we are using here is the RSpec module Spec::Expectations. In features/support/env.rb we have the line require 'rspec/expectations', thus we have the module at our disposal in all our step files. This module gives us the ability to express expectations on any object by adding methods to all objects (by monkey patching). We are using object expectations here, which provides methods like should == and should_not == (and some more) which are nothing more than syntactic sugar for saying that you expect one value to equal another value. Therefore, because this methods have been added to all objects and also because in Ruby really everything is an object – even a numeric value like the length of an array – we can call the method should == on the length of the array @choppedCucumbers.

The step definitions are quite silly (actually, the whole scenario is silly) and in this example we did not even test any production code – all the code is in the step file. But some of the basic concepts behind Cucumber should have become clear now.

Remark: We need to call the method to_i in two places because the argument that comes from the regex-match is always a string. We could get rid of this nuisance by using a step argument transformer if we wanted.

Fine, and now?

That should cover the basics of Cucumber, so let’s stop toying around with silly examples. Proceed to part two to test a real web application.

If you have questions or comments regarding the setup or the basics of Cucumber, please drop a line below.

  • Facebook
  • Delicious
  • Digg
  • StumbleUpon
  • Reddit
  • Blogger
  • LinkedIn
Bastian Krol

17 Responses to Cucumber: An Introduction for Non-Rubyists – Setup and the Basics

  1. Robert Flatters says:

    This is was an interest way of showing how cucumber works in both the writing acceptance test and then writing and understanding the way ruby program works.

  2. pixieluv says:

    Under Implementing Steps and line Given /^a Cucumber that is (\d+) cm long$/ do |length|

    If you leave Cucumber capitalized it will not give the intended results; however, changing it to cucumber works perfect

  3. sangeetha says:

    Great! A very good start for cucumber beginners.

  4. Very nice tutorial. It gives me the confidence to start studying BDD tools.

    Thanks a lot,

  5. Ladi Oyebo says:

    Great tutorial, easy to follow.

    Many thanks.

  6. kalyan says:

    I would like to know what are the advantages in cucumber when compared to selenium webdriver
    I am very familiar with selenium webdriver-TestNG but I would like to learn cucumber ruby

    • Bastian Krol says:

      Hi Kalyan,

      I do elaborate a bit on this in the follow up to this blog post (https://blog.codecentric.de/en/2013/08/cucumber-capybara-poltergeist/).

      I would not compare Selenium to Cucumber – Cucumber just gives you a BDD framework, in which you can use either Selenium or Capybara or a dozen other testing tools. That is, Cucumber helps with writing very readable scenarios, not more, not less.

      Instead, compare Selenium with something like Capybara:
      Selenium acts on a lower level of abstractions. Capybara (and other high level web acceptance frameworks) hide some of the details and offer a much better API than Selenium directly.

      In my very personal opinion, Selenium is not a good layer to use directly. For example, testing AJAX is quite hard and cumbersome with Selenium. It’s hard to write maintainable tests with Selenium. Capybara (and other testing frameworks, some of them which build on Selenium) makes a lot of things a lot easier.

      For this aspect, it does not matter so much if you use Capybara with Cucumber or with RSpec, for example.

      Hope that helps

      Bastian

  7. HI,

    New to Ruby cucumber, trying to learn the same, when i try the cutting vegitable, am getting following error, please let me know the issue where i made a mistake.

    thanks,
    jayasankar

    E:\Ruby200\samples\audiobook-collection-manager-acceptance-master>cucumber
    #encoding: utf-8
    Feature: Showcase the simplest possible cucumber scenario
    In order to verify that cucumber is installed and configured correctly
    As an aspiring BDD fanatic
    I should be able to run this scenario and see that the steps pass (green like a cuke)

    Scenario: Cutting vegetables # features\first.feature:8
    No connection could be made because the target machine actively refused it. – connect(2) (Errno::ECONNREFUSED)
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:878:in `initialize’
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:878:in `open’
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:878:in `block in connect’
    E:/Ruby200/lib/ruby/2.0.0/timeout.rb:52:in `timeout’
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:877:in `connect’
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:862:in `do_start’
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:851:in `start’
    E:/Ruby200/lib/ruby/gems/2.0.0/gems/rest-client-1.6.7/lib/restclient/request.rb:172:in `transmit’
    E:/Ruby200/lib/ruby/gems/2.0.0/gems/rest-client-1.6.7/lib/restclient/request.rb:64:in `execute’
    E:/Ruby200/lib/ruby/gems/2.0.0/gems/rest-client-1.6.7/lib/restclient/request.rb:33:in `execute’
    E:/Ruby200/lib/ruby/gems/2.0.0/gems/rest-client-1.6.7/lib/restclient.rb:84:in `delete’
    E:/Ruby200/samples/audiobook-collection-manager-acceptance-master/features/support/storage.rb:5:in `delete_database’
    E:/Ruby200/samples/audiobook-collection-manager-acceptance-master/features/support/hooks.rb:5:in `Before’
    Given a cucumber that is 30 cm long # features/step_definitions/first_steps.rb:3
    When I cut it in halves # features/step_definitions/first_steps.rb:7
    Then I have two cucumbers # features/step_definitions/first_steps.rb:14
    And both are 15 cm long # features/step_definitions/first_steps.rb:18
    No connection could be made because the target machine actively refused it. – connect(2) (Errno::ECONNREFUSED)
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:878:in `initialize’
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:878:in `open’
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:878:in `block in connect’
    E:/Ruby200/lib/ruby/2.0.0/timeout.rb:52:in `timeout’
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:877:in `connect’
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:862:in `do_start’
    E:/Ruby200/lib/ruby/2.0.0/net/http.rb:851:in `start’
    E:/Ruby200/lib/ruby/gems/2.0.0/gems/rest-client-1.6.7/lib/restclient/request.rb:172:in `transmit’
    E:/Ruby200/lib/ruby/gems/2.0.0/gems/rest-client-1.6.7/lib/restclient/request.rb:64:in `execute’
    E:/Ruby200/lib/ruby/gems/2.0.0/gems/rest-client-1.6.7/lib/restclient/request.rb:33:in `execute’
    E:/Ruby200/lib/ruby/gems/2.0.0/gems/rest-client-1.6.7/lib/restclient.rb:84:in `delete’
    E:/Ruby200/samples/audiobook-collection-manager-acceptance-master/features/support/storage.rb:5:in `delete_database’
    E:/Ruby200/samples/audiobook-collection-manager-acceptance-master/features/support/hooks.rb:12:in `After’

    Failing Scenarios:
    cucumber features\first.feature:8 # Scenario: Cutting vegetables

    1 scenario (1 failed)
    4 steps (4 skipped)
    0m4.797s

    • Bastian Krol says:

      Seems you’re on the master branch of audiobook-collection-manager-acceptance. If you just want to run the first feature (the cutting vegetables example) you should do

      git checkout 02_step_definitions

      and

      git reset --hard

      The master branch contains all cucumber features, including those testing a web application, as explained in the second part of this blog post. For those, you need to set up some more stuff (a little web server that serves the application under test).

      The error you see comes from a cucumber hook trying to clean up the test database (by calling the storage service per http) after the executing tests. You did not set that up and also don’t need to because it is irrelevant for this step of the tutorial.

  8. Kumar says:

    Thanks indeed for such a brilliant and clear explanation. Hats Off buddy…

    I am having one question. Please correct em if I am wrong.
    Comparison between Cucumber & Calabash

    1> CUCUMBER -a container or framework supports BDD
    -can plugged in with supported tool like Selenium ,capybara or even Calabash

    2> CALABASH-it’s a non GUI TEST AUTOMATION tool. It can not have it’s standalone existence like QTP(UFT). Must be used with only CUCUMBER.

    Can you please throe some light on this? Is this Correct?

    Regards,
    Kumar

  9. Bastian Krol says:

    1) Yes.
    2) Um, what do you mean by non GUI? I would say it’s a GUI test automation tool (for Android and iOS apps).

  10. Anunaya thakur says:

    I am trying to clone
    git clone -b 00_setup https://github.com/basti1302/audiobook-collection-manager-acceptance.git

    But it asks for user id and password.
    What is the user Id & password to clone.

    $ git clone -b 00_setup https://github.com/bastil302/audiobook-collection-manag
    er-acceptance.git
    Cloning into ‘audiobook-collection-manager-acceptance’…
    Username for ‘https://github.com': anunay1
    Password for ‘https://anunay1@github.com':
    fatal: https://github.com/bastil302/audiobook-collection-manager-acceptance.git/
    info/refs?service=git-upload-pack not found: did you run git update-server-info
    on the server?

    • Bastian Krol says:

      Usually git should only ask for user/password when you try to push to the repo. git clone should work without credentials. I just double checked that, I do not need to provide my usename/password when doing git clone. Maybe you have an unusual git config setting or something? Could you try the command on a different machine, just to check?

  11. Elliott Fitzgerald says:

    Thanks this was a great introduction to Cucumber

  12. thu nguyen says:

    More example of Cucumber with Selenium Ruby on Window:
    http://huutoan123.blogspot.com/2014/08/abc.html

  13. Danilo says:

    Excelent post! Very Helpful!!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>