21 August 2012

Cucumber Variables in Tests and using "Examples"

Back in my posts long ago, I had mentioned something about GEB tests using the Where clause.  The where clause added a table of data in the test, that the test would iterate through.

Similarly in Cucumber, it's called Examples, and it's invoked using a Scenario Outline.  A basic Cucumber test has a Feature, which has a Scenario, which has steps. 

In a case where you want to run different values, you would use a Scenario Outline.  This works like this:

Feature: Cross Browser use
     Scenario Outline: Users hit the same site in multiple browsers
          Given a user goes to www.whatsmybrowser.com in "<browser>"
          When they click the check my browser button
          Then the output on the screen should be "<output>"

     Examples:
          | browser | output  |
          | ff           | firefox  |
          | ie           | IE         |
          |chrome   | Chrome|

By doing this, we allow the BDD test itself to select the variables for browser and output.  This way several iterations of the test could be run against one test. 

Video of Cucumber Test Running



20 August 2012

Test Cleanliness - GEB vs Cucumber

Look how clean the BDD portion of the cucumber tests are, compared to something like GEB:

Cucumber:
Feature: User searches for book on Amazon.com
  Scenario: User searches for the book Cucumber
    Given a user goes to Amazon.com
    When they input the title Cucumber in the Amazon search field
    And they click submit
    Then  they should see a results list with Cucumber in the title

   Scenario: Amazon user adds The Cucumber Book to their shopping cart
     Given an Amazon user who has selected the Cucumber book
     When they click add to cart
     Then the book is added to their shopping cart

GEB:
 given: "a newly registered user"
            user = newUser(GENDER, USER_DETAILS, [country: cy, postalCode: postalC])
            def locales = [us: "US", aa: "Austrailia", gb: "England", ca: "Cananda", gr: "Germany"]
            getBrowser().reportGroup(getBrowser().reportGroup + "/" + locales[cy] + "/" + pMethod)

        when: "he attempts to purchase product #1"
            login()
            productAd.click()
            sleep 500
            $("input#prod_price").click(ProdPage)
            $("input.agree-accept").click(BillingPage)
            submitBillingInfo(paymentMethod)
                    .confirmPurchase()
                    .completeProductFlow()

...man, that's a lot of ubly BDD code, lets just stop there.  Each of those calls goes to a class or page, that has all the core functions.  So you're looking at lots of code and ugly BDD tests with GEB.

While GEB is readable, it's messy.  Consider Cucumber and how clean it is.  In Cucumber all the real code is in the step_definitions.  It makes the BDD tests so nice to see and read. 

15 August 2012

Cucumber set up and sample test

Compared to the time I spent getting GEB up and running and getting tests working... Cucumber is so much easier.

Here's how I set up Cucumber
First, I edited the Gemfile for a project and added this group:

group :test, :development do
  gem 'cucumber-rails'
  gem 'database_cleaner'
  gem 'rspec'
  gem 'spork'
  gem 'watir-webdriver'
end

Once done, I run at the project root:
bundle install

It pulls down the gems needed - in this case, I'm mainly using cucumber-rails and watir-webdriver.

After the install, I run a rails generator call to set up cucumber from the project root:
rails generate cucumber:install

This will create a features folder... it's layout will be like this:
features
  |
  |--step_definitions
  |--support

At the root of the features folder, I put my BDD test.  This is where you define the goals... you can think of it as the high level "Given When Thens."  What's cool about Cucumber, is that the NO code goes in this file, it's simple.  In GEB, or groovy frameworks, simple code goes in the BDD tests.  But in Cucumber, NOTHING but the BDD goes there. 

The BDD Test

In my test, I wrote the following in a ruby file at the root here:
Feature: User searches for book on Amazon.com
  Scenario: User searches for the book Cucumber
    Given a user goes to Amazon.com
    When they input the title Cucumber in the Amazon search field
    And they click submit
    Then  they should see a resuls list with Cucumber in the title

So the directory looks like:
features
  |
  |--step_definitions
  |--support
  amazon_test.rb

If you were to run the command "cucumber" now, it would return back a statement that there are no step definitions.  Which is true, there aren't.  The step definitions is the code that makes this test work.  Right now, it just has the BDD language, but no code.

The Step Definitions
So lets create the amazon_test.rb ruby file... in there I use Watir-webdriver to power the browser... and I use Cucumber to write the Given When Thens'.  Here's my file's contents:
Given /^a user goes to Amazon.com$/ do
  @browser = Watir::Browser.new(:ff)
  @browser.goto "http://www.amazon.com"
  @browser.title
end

When /^they input the title Cucumber in the Amazon search field$/ do
  @browser.text_field(:name => "field-keywords").set "Cucumber"
end

And /^they click submit$/ do
  @browser.button(:class => "nav-submit-input").click
end

Then /^they should see a resuls list with Cucumber in the title$/ do
  @browser.h1(:id => "breadCrumb") == "Cucumber"
  @browser.close
end

Now if we run "cucumber" - it will execute each scenario in the test and each part of the test.  If it fails to get a value for any of the steps, it will return the error in the console.  If you have a ANSI color coded terminal you'll get results in a nice color coding.

Breaking the Code Down
First line defines the browser I'm using. In this case it's Firefox.

After that I tell it where to go, in this case www.amazon.com

Next I'm finding the main search field on Amazon ("field-keywords") - I'm searching for the name on the field.  It would be better to search by ID, but I'm going for a field name search.  In the same line, I'm passing the value of "cucumber" into the field.

Next I find the submit button and click it.

Then I find the breadcrumb on the page, and verify the value is "Cucumber"

Finally I close the browser to end the test.

Rails Webapp: WoW Lookup

I've been busy with a family situation.  But in my downtime, late at night, I've been learning and writing code.  I came up with a web application that I created in Rails.

Below I'll provide a summary of what the webapp does, and then a step through of the program.

The program itself is housed at:
https://github.com/wbwarnerb/wowlookup

You can see this in action at my Heroku publish point:

What it does:
I wrote a web application to test some items I learned. The application ties to a public available API from World of Warcraft.  The API I'm talking to is a Quest API.  It's a simple REST call, that needs a ID appended to the URL.  World of Warcraft responds with JSON.  I convert the JSON into a hash, and then finally output specific elements of the HASH to a results view.

Step Through:
I modified the routes file to point the app to search#index.
From search#index, a view is presented with a very simple form.  The form has only one text field, and a submit button.
When a number is input into the field and a user submits, the number is added as a parameter to the URL being called to World of Warcraft:
This is the WoW URL:
http://us.battle.net/api/wow/quest/
Adding a number to the end, will call the WoW database for quest info for that quest id.

I make this call, and associate it to a instanced variable in the controller, like this:
@output = JSON.parse(open("http://us.battle.net/api/wow/quest/"+params[:qid]).read)

The instanced variable is @output.  The code: JSON.parse is going to parse the results of the URL we're calling.  Next the code opens a URL, and I'm passing the ID the user input in the text field with: +params[:qid].  the .read at the end, is just the return of the value.

The data that returns, has some useful info and some not so useful.  I take the hash that's returned from World of Warcraft (it looks like this:
{"id":9877,"title":"A Restorative Draught","reqLevel":17,"suggestedPartyMembers":0,"category":"Ghostlands","level":20})
and I create a results view.  In the results view I added:
<%= @output['title'] %>
<%= @output['level'] %>
<%= @output['suggestedPartyMembers'] %>
<%= @output['category'] %> 

At the bottom of the results view, I added a link to go back to the search page:
<%= link_to("Go back to Search", { :controller => "search", :action => "index" }) %>

 The code to go back is simple enough. It's just saying Link this text "Go back to Search" with a link to the controller (search) and action (index) - which looks like "/search#index"
 
 

11 August 2012

git fetch vs. git pull

When wanting to overwrite local files, I found using git fetch is the answer.

It has to be followed by:
git reset --hard origin/master


09 August 2012

First Webapp

During my lunch break today, I opened up Ruby on Rails Tutorial and walked through setting up another first time app.

So this is my 2nd app i'm working on.  I wanted to actually do something... so I decided to get something simple going and publish via Heroku.

So what's Heroku?
Heroku is this awesome cloud service, that's free, if  you're just testing your apps.  you go to heroku.com and set up an account... then you run a rails gem like: gem install heroku
and that will install it.

The idea is this:
1. you write some code
2. you commit the code locally with git commit
3. you push the commit to a remote git repo (i.e. github.com)
4. you make a call to Heroku to pick up the git repo and publish it.

I hit a few snags from the book though.  The book didn't tell me I had to change the gem sqlite3.  Heroku doesn't like it. The solution was to go into the Rails Gemfile and do something like this:
group :development, :test do
   gem "sqlite3"
end

If you don't set up a dev group like that, then it tries to compile sqlite3 on Heroku and since it has it's own db structure it will fail and error out (the error would be "an error occurred while installing sqlite3...."

So here's my webapp:
http://peaceful-castle-5270.herokuapp.com/users

Heroku makes a random name for your webapp.  That /users is the code I comitted and published. it allows users to be created based on their email address.

So how did I do it?

Well I cheated somewhat.  I used scaffolding.  In Rails there's a scaffolding option.  It will build out a structure for your application.  All I had to do, was call a Users model with the scaffolding and it built it out...
rails generate scaffold User name:string email:string

That one command did all of this.  It created the User model and set up a name and email variable as a string the user inputs.  It added in all the good stuff.

Once done, you run a rake migrate, like:
bundle exec rake db:migrate

Add this to Git: git add .
Commit with git: git commit -m "something"
Push to github (should have defined this already): git push
If you have created the Heroku space, you run:
heroku create
Publish with Heroku: git push heroku master

You're done.  Go to the url

08 August 2012

Review of the GEB Test Framework

Automation test frameworks should be able to meet the needs of a QA team.

After working with GEB for over a year now (almost two years), I've come to the opinion, that it's not a very good solution for QA.  GEB is a very brittle and the Groovy language has some barriers to entry for people who are not professional developers (i.e. QA.)

There is some talk in some companies that any QA person should be technical enough to fill in and code for any developer... but it's an bizarre concept - that leads to QA automation strategies, such as GEB.  If a QA person could fill the shoes of a developer, they'd switch titles and make 20% more money.   This Free Market Economy establishes our roles and responsibilities.  I've even heard some places where Project Managers are expected to be able to code and fill in for a developer.  If they really could be the same or even similar quality, why wouldn't they just switch titles and make more money?

Initially I thought it was a great idea (bringing GEB into the field of view.)  I thought it made sense.  It utilized the skills of the entire development team (since they already knew Java) and gave us momentum.  But as you'll see, my view of the fruit of this, almost two years later, isn't very positive.

What is GEB?
GEB is a automation framework built on three things:
Groovy
Spock
Webdriver

Brittle
I mentioned GEB is brittle.  This is of course my opinion.  Some of my friends and coworkers disagree on this point.  But I've seen nothing great from this framework.  Our tests are constantly broken.  Granted one could say "well it's your tests!"  Possibly, but there's so much problems with GEB.  For starters GEB doesn't have a strong community.  It gets few updates.  The framework is so outdated, where QA is concerned.  While Firefox releases a browser every couple weeks, GEB slips further and further behind.

GEB has old requirements. It needs specific old versions of Groovy (1.7 to be exact.)  Groovy has already jumped two versions now (from 1.7, to 1.8 and now 2.0)  But GEB won't work with any Groovy compiler above 1.7.    The same is true with Spock. GEB only works with Spock 0.67.

This means, if you grab a IDE like STS, you need to go back about 15 versions to 2.7.1.  Anything above that preinstalls Groovy 1.8 or higher.  It also means you have to watch any auto updates.

What happens when Firefox comes up with a new version? you need to run through a gamut of trial and error to upgrade a simple selenium webdriver in the POM and then find out it's not compatible with the brittle framework.  You wait for a solution. Get it, install it and Firefox is up another version!

What about webdriver... GEB doesn't like IE much.  It can't seem to close the sessions.  So we can't run this via Jenkins - as the IE sessions stay open and kill the server.   GEB uses old versions of selenium webdrivers... it has to use versions like 2.23 for IE... and the current version as of writing this is 2.4! 

You could spend your time 100% grossed in the framework, or get a framework that just works and spend your time writing tests.  I prefer the latter.

Barrier To Entry
GEB uses the Groovy language.  Groovy is very easy to get and understand, but all the resources for Groovy are written for experienced developers.  I like to think of Groovy as shorthand for Java dev's.  The problem here is, most QA teams are not made up of professional and experienced Java developers.  If they were, they would switch titles in a heartbeat and make more money.   Without a strong community pushing Groovy to those with little programming experience, Groovy becomes difficult to learn.

It's much like wanting to learn how to write Chinese and someone tells you there's a shorthand for Chinese, it's really simple... but unfortunately you have to learn Chinese first, in order to understand the shorthand!

GEB Setup
I work at a shop that first built it's own Groovy/Spock framework.  It took months.  Then we found GEB.  1 Architect and 2 dev's took 3 months to get the thing up and running.

I attempted the same thing at home. Despite all the git repo's with "geb sample project" - none of them worked for me.  I couldn't get the damn thing working for months.  I think I finally got it up and running in about 3 months. 

Installing Ruby and Watir, and writing a test took me 10min.

What Other Options?
Where GEB can't seem to close a IE browser it opens in a test, Watir and Cucumber, just work.  They pull up every browser I have. They can use the latest webdrivers... Where GEB needs old archaic versions of selenium webdrivers.

I wont begin to pretend I'm an expert in a variety of frameworks.  But I will say that I installed Rails, and did a gem install watir and was able to write a test within 10min.  This is impossible with GEB and it's brittleness - which took me months to get working and years of frustration of broken tests and environments.  

Scopes

Scopes
Scopes allow you to define a query in a model.
These can be called like ActiveRelation methods
These can accept parameters

Example, you could go to a model, like subject.rb and add:
scope :visible, where(:visible => true)
The symbol :visible is just that, a symbol. it could be anything, it's just a reference to how we can call this.

So it's saying, :visible will run where visible is true. 

back in the console, you could run Subject.visible
it will return all records that have the value visible as true.

Rails: Handling Records (create, update, delete, conditions)

Creating records
There are two ways to do this but the easier way is to use the create method:
you would instantiate the class, and pass in the hash for the values you need...
Example:
subject = Subject.create(:name => "Subject name", :position => 2)

Create auto saves. 

Updating records
two ways, you could find, set and save
or use update_attribues which finds,sets and saves in one step.

the first way you would find like:
subject = Subject.find(1)
subject.name = "initial Subject"
you can verify with
subject.name
but at this point, the db hasn't been updated.
so you would run
subject.save and it would update the database.

The simpler way is to use:
First find the attribute and instantiate the object like:
subject = Subject.find(2)
Then do the update and save in one step:
subject.update_attributes(:name => "Revised Subject", :visible => true)

Deleting Records
First lets create a record to delete
Subject.create(:name => "Bad Subject")
Lets find it:
subject = Subject.find(3)
Now we can delete it with:
subject.destroy
This removes it from the database, but if you're doing thsi in Rails console, you still have access to it... you could do a
subject.name and there it is.
This hash / record exists in the console, but it's frozen, you can't modify it.  It was removed from the database.

Finding Records
To simply find a record you can use this format:
classname.find(id)
example: Subject.find(2)
this simple method will return the object, or an error

Dynamic Finders
This is useful when you want to searched based on criteria other then ID.
it is structured like this:
Classname.find_by_[indentifier]([value])
Example:
Subject.find_by_name("First Subject")
This dynamic method will return the object or nil.
This method may be depreciated at some point, due to the enhanced queires in Rails.

Find All
Subject.all
This returns an array of objects

First/Last lookup
Subject.first, Subject.last
returns an object or nil

Query Methods
This uses the ActiveRelation query methods
Example:
Subject.where(:visible => true)
The ActiveRelation query uses "where"

Conditions allow:
Strings
   Strings pass raw SQL
   This is vulnerable to SQL injection
     Malicious code could be injected

The way to handle SQL injection attacks is to escape the SQL using an Array

Array
instead of SQL, we escape it like:
["name = ? AND visible = true", "First Subject"]
This builds a safe string.

Hash
Example:
{:name => "First Subject", :visible => true}
This is fine, but it doesn't handle all SQL possibilities like Arrays.

Which to use?
Hash, until you need to use something more complex, then use the Array.  Avoid Strings.






07 August 2012

Rails: ActiveRecord and ActiveRelation

ActiveRecord
"active record" is a way of writing software (a software design pattern. )
"ActiveRecord" is a rails implementation of  active record.

Basically, it retrieves and manipulates db data as objects and NOT just as static rows.
ActiveRecords lets the objects
  • understand the structure and 
  • they know how to interact with it.  
  • They contain data but also have the ability to create, read, update and delete rows.

Examples of ActiveRecord:
To do a SQL Insert:
user = User.new
user.first_name = "Brian" 
user.save

To do a SQL Update:
user.last_name = "Warner"
user.save

To do a SQL Delete:
user.delete

ActiveRelation
This is new in Rails 3
This simplifies the generation of complex db queries.
Also, it handles the timing of queries, so these queries execute when needed.

Example of ActiveRelation:
Instead of the SQL:
select users.*, articles.*
from users
left join articles on (users.id = articles.author_id)
where users.first_name = 'Brian'
order by last_name ASC Limit 5;

it would be written like this:
users = User.where(:first_name => "Brian")
users = users.order("last_name ASC").limit(5)
users = users.include(:articles_authored)

So it shrinks down the SQL queries to some simple Ruby lines.

Rails Migrations (page 2)

Rails Migrations p2
to roll a migration back, you run rake like this:
rake db:migration VERSION=0 (0 tells it to go back to the beginning.)

if you want to target a version, or environment you would do
rake db:migration VERSION=[some version], RAILS_ENV=[some environment]

The version numbers are in the "schema_migrations" table in the db.  They are also the numeric numbers on the migration files themselves.

How it works:
By default, if you have multiple migrations (say 7 versions), and you only want version 5... you would specify the version, it will run a db migration for version 1, then version 2, all the way up to the target version.

You can also do a rake:db:migrate:up and specify the version, this will only run that one migration version.

rake db:migration:redo rolls back to the previous then up to the last one.

Rails: Migration (page 1)

Rails Migrations...
are a set of db instructions
written in Ruby
Allows you to migrate your db from one state to another
Contains both move up (to new state) and down (to a prev state)

Why use it?
Migrations keep the schema with the application code

The Migration keeps all the db schema changes... and allows sharing of schema changes.

Different users or different computers can easily set up the db to the same state

Inside the migration file it will create the table setups.  A string (i.e. t.string "first_name") will be seen as a varchar and create a column with this name.

create_table format
t.[type]
example: t.string "Name", options
Different types are: binary, boolean, date, datetime, decimal, float, integer, string, text, time.

Special Columns
created_at and updated_at are special columns in rails, you just call it out in the migration (i.e. t.imestamps) and these two columns are automatically created.

Running a migration in Rails
rake db:migrate
This runs all the migrations you have available... The output of which, looks something like this:
c:\Sites\simple_cms2>rake db:migrate
==  CreateSubjects: migrating =================================================
-- create_table(:subjects)
   -> 0.0780s
==  CreateSubjects: migrated (0.0800s) ========================================

==  CreatePages: migrating ====================================================
-- create_table(:pages)
   -> 0.0170s
-- add_index("pages", "subject_id")
   -> 0.0210s
-- add_index("pages", "permalink")
   -> 0.0770s
==  CreatePages: migrated (0.1200s) ===========================================

==  CreateSections: migrating =================================================
-- create_table(:sections)
   -> 0.0230s
-- add_index("sections", "page_id")
   -> 0.0200s
==  CreateSections: migrated (0.0450s) ========================================

==  CreateUsers: migrating ====================================================
-- create_table(:users)
   -> 0.0050s
==  CreateUsers: migrated (0.0050s) ===========================================

==  DoNothingYet: migrating ===================================================
==  DoNothingYet: migrated (0.0000s) ==========================================

After the migration, we can log into the database and check things out...
if we run the SQL:
show tables
we'll see our tables...
If we check the table we did some migration specifications with by running the SQL query:
mysql> show fields from users;



We'll get some output like this (which shows all the columns we set up in the Migration):
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| first_name | varchar(25)  | YES  |     | NULL    |                |
| last_name  | varchar(50)  | YES  |     | NULL    |                |
| email      | varchar(255) | NO   |     |         |                |
| password   | varchar(40)  | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

06 August 2012

setting up and using git with a public repo

I built out my first git remote repo - and published some code to it... It wasn't as hard as it seemed to be... it seemed intimidating at first... but it wasn't that bad... I just followed this tutorial on git:
http://www.vogella.com/articles/Git/article.html#firstgit_content

the repo is:
git://github.com/wbwarnerb/demo-project.git


Rails Routing

Routing Types are simple, default and root.




Simple
An example would be like:
                   get "demo/index"
which is a shortcut for
                   match "demo/index", :to => "demo#index"
this is saying when you get a request to go to: demo/index, to send to the controller#action (i.e. demo#index.)

Default
This separates the controller action and id with /
i.e. match ':controller(/:action(/:id(.:format)))'

Root
this is a similar match, but for the root of the application... such as:
root :to => "demo#index"
so this would say, when you hit the root of the application in a URL, it will route from this root to the controller#action.

Views and Erb's



ERB = Embedded Ruby.  it goes into the html pages and either executes or executes and outputs the result.

<% code %> - executes the code
<%= code %> - executes then outputs the code

04 August 2012

SOLVED: Rails setup error with mysql

I had this problem setting up rails and mysql2.  Even though I had a database in Mysql, and a user created with appropriate rights granted... i would always get this error when hitting the rails app I made:
Can't connect to MySQL server on 'localhost' (10061) mysql

I searched and searched. I tried a variety of solutions, but then ran across one seeming odd solution.  There was a simple post on a website about going into the /config/database.yml file and changing the line:
host: localhost
to
host: 127.0.0.1

THAT WORKED.

How crazy is that?  The webserver knows localhost is 127.0.0.1, but for some reason this needed to be specified to an IP Address in the database.yml file!!

03 August 2012

Gem compilation error in rails.

I hit this error after setting up MySQL and trying to run the rails server:
this gem was compiled for 6.0.0 but teh client library is 5.*

discovered the solution here:
http://stackoverflow.com/questions/8740868/mysql2-gem-compiled-for-wrong-mysql-client-library

once you install the connector, you copy the libmysql.dll from the connector directory to the ruby bin in the rails directory.

Fixing MySQL install errors

http://www.literateknolohitura.com/2011/06/solution-to-error-number-1045-mysql.html

01 August 2012

Rails Basic's Notes

So what is rails?
it's a web app framework for ruby's

That means it's a set of code libraries that can provide general functionality that can be used, and reused. It also has default values built in and flow controls.  Libraries usually don't set default values.




Rails uses MVC Architecture 
MVC architecture stands for: Model View Controler

The Model is the objects

The View is the presentation layer (what the user sees and interacts with)

The Control processes and responses to events - such as user actions

So...
Decision code goes in the controller

Data code goes in the model

Presentation code goes in the view

Rails refers to the view as the action view

Rails refers to the model as the active record

Rails refers to the model and view together as the "action pack"