29jul2009
por
Koiti Takahashi em
advanced,
amqp,
broker,
communication,
english,
fifo,
integration,
message,
protocol,
qpid,
queue,
rabbit,
rabbitmq,
server
After a little more than one year of heavy code production and few innovations, although I could explore some interesting stuff like WCF and RESTful, our team finally got a time to “invent” something new. This time, we have to rewrite a legacy system (mainly a single stored procedure *arghh*) that verifies the clients with overdue payments, notifies them about it and stop provisioning services until their debts are paid off. As it can be imagined, it has many responsibilities belonging to different domains, consequently, belonging to different teams of our company.
My team is responsible for verifying the financial issues and should inform another team, which takes care of services provisioning, about the debtors. Thus, it is necessary to define how our systems will communicate with each other. One of the suggested solutions was AMQP standard for message queuing and that’s exactly what I will talk about.
AMQP stands for Advanced Message Queuing Protocol and its main purpose is to enable full functional interoperability between conforming clients and messaging middleware servers (aka. brokers). That is, AMQP defines a standard protocol for publishing, queueing, storing and consuming messages. The message producer utilizes a client API to publish message into a server. The server/broker will then place the message into a queue and store it while it is not consumed. At last, a consumer application will accept the messages stored in a queue and remove them from it.
The AMQP overall model is represented below:

Server
An AMQP server, like RabbitMQ or Qpid, is a data server that accepts messages, routes them to different consumers (queues) according to some arbitrary criteria (bindings) and store the messages while not consumed.
Virtual Hosts
A virtual host is somewhat similar to a workspace where a user connects. It comprises a set of exchanges, message queues and bindings. Even though an user is authenticated into the server, it may not successfully connect into a virtual host if it is not authorized. The authorization for the server users are granted for each virtual host.
Exchange
It can be said that the exchange possess the most important role among the AMQP middleware server components. It is delegated to route the messages to the queues matching the routing information, like an routing key, with the previously set bindings. Exchanges can also route messages according to its type. Some of them are explained below:
- Direct: direct type exchange routes a given message matching exactly the message routing key with the queue binding key. This type of exchange is a MUST in AMQP server implementation.
- Fanout: fanout type exchange routes a given message to all bound queues regardless of it’s routing key. The fanout also is a MUST in the broker.
- Topic: topic type exchange routes a given message verifying if the message key matches the queue binding key. For instance, let’s suppose that we have created a queue belonging to an exchange of type topic and it is bound to the exchange with a binding key like “sport.#”. Now, imagine a message sent to the exchange with a routing key “sport.soccer”. The key “sport.#” says that any message with a key “sport.anything” must be routed to the queue with such binding key. The ‘#’ means matches zero or more words and ‘*’ means matches one word. Topic is a SHOULD be implement type in AMQP server.
Bindings
A binding is a constraint between a queue and an exchange, setting a criterion for message routing.
Routing Keys/Binding Keys
As explained above, routing keys are message identifiers. In the other hand, binding keys are bindings identifiers. Duh, you may say. I agree. But it is pretty much that. Binding keys exists to tell the exchange that a message with a key that matches the routing key should be addressed to its related queue.
Message Queues
Message queues are the consumer’s message inboxes. It keeps the messages in memory or disk until they are read by the consumer in a FIFO politics.
To illustrate AMQP messaging flow in a more comprehensible manner, let’s take a look at the following comic (special thanks to KreuZ for helping me with the figures), representing a Direct Type Exchange:



In these frames, we have a Message producer that sends a message with a “cars” key to the Exchange. We have also two queues: John’s queue and Kate’s queue. John’s queue says that he likes “cars”. That is, he is bounded to the exchange saying that any message related to “cars” (binding key) is of his business.
When the Exchange receives the given message, he knows that John’s queue is interested in "cars”, Kate’s queue is interested in “cookies”, and that he has a message about “cars” in hands. So, with these information, he passes the message to John’s queue but not to Kate’s queue, since the message “subject” matches exactly with what John’s queue wants.
Well, that’s an introductory post about AMQP that I wanted to have it here because my next post will be about real code using AMQP implementations. For more information about AMQP, please refer to its site.
Finally, I hope that the illustration helps for a better understanding of the basic principles of AMQP protocol and I would appreciate your comments and mainly, suggestion and critics.
Link deste post
27jul2009
I’m sorry my last post was about three months ago. But, I have a good excuse: I’m just married!
(and I took a nice and fast honeymoon vacation)
Besides that, after three happy years, full time at Caelum, here is the shocking news: I’m now part of the Locaweb team!

This was (and is still being) a very hard decision. People close to me know, that I have a nice and strong relationship with Caelum. Man, I love the company!
I’m feeling very strange, because I’m sad I’m no more full time at Caelum and, at the same time, I’m extremely excited with the new challenges which are about to come. My decision to join Locaweb, which is a really good place to work, just proves I’m very anxious to make a good job there. Other than that, I’m going to sit near very known people as my friend Fabio Akita and Daniel Cukier, just to cite some.
I’m joining the talented Cloud Computing team and I hope I can help them to improve the Cloud Server product. An enormous responsibility!

Locaweb is a huge company and I must admit I’m a little bit scared with the size of things there. They have many teams working on a big diversity of challenging products. They even have their own Datacenter! Locaweb already embraced agile and people there are very open minded, because as a hosting provider, they have to deal with almost all kind of technology.
Because of my work prior to Locaweb, I know the expectations on me are quite high. I like being honest and just to be clear, I’m not better than anyone. I’m relatively new to this area, so I still have too much to learn. Fortunately, I already know many people at Locaweb and I really believe they will help me start being productive. Additionally, there are areas I know I can give something. I’m joining to help, not to prove anything to anyone.
It’s also important to say, that I couldn’t fully leave Caelum. I’m still part of the team, as an instructor, and I keep helping them in many areas, such as the book, courses, textbooks, internal discussions, events, talks and where else I can.

That’s it. Comments, questions and feature suggestions are, as always, welcome. As a cloud provider, I’m happy to hear what would you like to see in a cloud product and what can we do to help you scale and earn profit.
From now on, I stop referring to Locaweb as “they” and instead as “we at Locaweb …”.
Posted in caelum, career, job, locaweb Tagged: caelum, career, cloud, deployment, job, locaweb
Link deste post
27jul2009
por
macskeptic em
Agile,
BDD,
C#,
code,
control,
CSHARP,
cucumber,
dependency,
dependencyinjection,
english,
fake,
framework,
injection,
inversion,
inversionofcontrol,
ioc,
mock,
of,
rails,
refactoring,
RSpec,
ruby,
spy,
stub,
taft,
tdd,
test
The "Beyond The Buzzwords" series is focused on giving those who are starting to write automated tests a practical overview, so that they begin to get a grasp on the mechanics of doing it. Also, to encourage those who don’t do it yet to begin doing so. That said, the idea is to not be over theoretical and purist, scaring away newcomers - but keeping both feet firmly on the arduous ground in which lies the path to solid automated testing practices.
This is the second article in the series. Today I’m going to talk about TDD - taking a slightly different approach on the bookstore application we developed on the last one.
TDD stands for Test Driven Design - it means to let your tests guide your software development at each and every step of the way to quality and highly maintainable code. It is mainly based on a relentless three step heartbeat-like pattern that must not be disturbed:
Red => Green => Refactoring => Red…
This very simple pattern stands at the very core of the TDD mindset. So, let’s take a look at the meaning of each work [if you have not guessed it already]:
- Red stands for writing a test that describes a single simple and punctual functionality, then running that test to watch it fails. Yes, you should [must] write your tests first, otherwise they’ll nothing but re-hashes of your actual implementation code, therefore testing nothing in terms of domain logic per se.
- Green stands for writing the bare minimum of code necessary to make the test pass. This is important, don’t jump to implement extra functionality just because you’re already coding anyway - keep it minimal, simple and focused on making the test pass, you can care about design later.
- Refactoring stands for making your code better, from a design perspective, applying some refactoring principle [Martin Fowler wrote a book on this subject, published in 1999 - with this being a pretty good catalogue of commonly used refactoring patterns]. That must only be done when all your tests are passing, and they should still pass once you’ve done refactoring - otherwise it means that your embellishment of the code actually broke it.
If you are willing to take the textbook approach to TDD, then the aforementioned definitions mean that not a single line of code shall be written if there isn’t a test failing because of the lack of that very specific line. And this is definitely a statement not to be taken lightly.
While BDD seems to lack support from proper tools for the modern widespread mainstream programming languages (JBehave and NBehave still have ways to go before standing up to cucumber) - TDD, on the other hand, has plenty of them for virtually any platform you pick. Since our bookstore is a rails application, we’ll be using the ruby option that I like the most: rspec.
Let’s just say we are given the exact same story to work with than last time, in case you’ve forgot it:
As the bookstore owner
I want to be able to register new books including rating and author
So that I don’t need to call the IT guys to do it for me manually every single time
That being said, let’s see how it works on practice:
rails bookstore_tdd --database=mysql
cd bookstore_tdd
rake db:create:all
rake db:migrate
script/generate rspec
The last command shown above shall generate some infrastructure for testing with rspec on your rails application.
Now, let’s create the book model, instead of the default rails generator we’re going to use the rspec one, like this:
script/generate rspec_model book title:string author:string rating:decimal synopsis:text
Also let’s create the books table both on development and on test environment
rake db:migrate
rake db:migrate RAILS_ENV=test
Since we’ve used the rspec generator for the model, it already created an skeleton so that we can test it, you can check it on the spec/models/book_file.rb file. It should look like this:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe Book do
before(:each) do
@valid_attributes = {
:title => "value for title",
:author => "value for author",
:rating => 9.99,
:synopsis => "value for synopsis"
}
end
it "should create a new instance given valid attributes" do
Book.create!(@valid_attributes)
end
end
Since the whole code for persistence of the book model is handled by the active record framework, we don’t need to bother to write tests to ensure that it works - since such test suite is already done by those who develop it.
But, for the sake of exemplifying the possibilities, let’s say that our business policy says that it is OK to have a book without synopsis and/or rating, but its title and author are mandatory properties. The previous statement describes a very specific rule to our application - the best way to express it is writting a test to make it clear. Well written tests can virtually replace any need for external documentation - in other words: your tests should describe as clearly as possible the particularities of your application.
Keep in mind that rspec is a very flexible tool, and can be used in many different ways to accomplish the same goal. I’m going to show an approach that I am particularly inclined to use very often. Let’s change the placeholder code that was automatically inserted on the book spec to this:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe Book, "with all attributes properly set" do
before(:each) do
@valid_attributes = {
:title => "value for title",
:author => "value for author",
:rating => 9.99,
:synopsis => "value for synopsis"
}
end
it "should create a new instance given valid attributes" do
lambda { Book.create!(@valid_attributes) }.should_not raise_error
end
end
describe Book, "missing rating and synopsis" do
before(:each) do
@valid_attributes = {
:title => "value for title",
:author => "value for author"
}
end
it "should be OK, since rating and synopsis are optional" do
lambda { Book.create!(@valid_attributes) }.should_not raise_error
end
end
describe Book, "missing author" do
before(:each) do
@invalid_attributes = {
:title => "value for title",
:rating => 9.99,
:synopsis => "value for synopsis"
}
end
it "should not be allowed, because author is mandatory" do
# lambda { Book.create!(@invalid_attributes) }.should raise_error <= this is another way to do it
book = Book.create(@invalid_attributes)
book.errors_on(:author).should_not be_empty
end
end
describe Book, "missing title" do
before(:each) do
@invalid_attributes = {
:author => "value for author",
:rating => 9.99,
:synopsis => "value for synopsis"
}
end
it "should not be allowed, because title is mandatory" do
# lambda { Book.create!(@invalid_attributes) }.should raise_error <= this is another way to do it
book = Book.create(@invalid_attributes)
book.errors_on(:title).should_not be_empty
end
end
That was a lot of code, so let’s take a look at some crucial concepts:
- With rspec you don’t write tests for your classes, you write a description about what is expected from them - therefore the use of the “describe” syntax
- Some people defend fewer contexts for each model, I particularly like to create a new describe block for each kind of scenario
- Rspec defines the methods “should” and “should_not” for every object, so you can evaluate the result using them - a full list of the possibilites can be consulted here
You probably have noticed that each test has been written using the prefix “it” followed by a natural language sentence, right? Because of that, the outcome we get when we run our specs [rake spec] is something like this:
FF..
1)
'Book missing title should not be allowed, because title is mandatory' FAILED
expected empty? to return false, got true
./spec/models/book_spec.rb:59:
2)
'Book missing author should not be allowed, because author is mandatory' FAILED
expected empty? to return false, got true
./spec/models/book_spec.rb:43:
Finished in 0.054108 seconds
4 examples, 2 failures
As I already stated before, from the excerpt shown just above - if you plan your tests to pinpoint each specifical detail of functionality you might even be able to never again need to run a debugging session. By looking at the result shown above we can notice the “FF..” on the beginning, it means that there are two tests failing [F] an two passing [.].
Since it failed, it is time to write the code to make it pass. On the book model, add the validation for the mandatory fields, like this:
class Book < ActiveRecord::Base
validates_presence_of [:title, :author]
end
Running the specs again show us that it worked:
....
Finished in 0.061223 seconds
4 examples, 0 failures
Homework: write tests to guarantee that duplicated titles are not allowed.
Now we need to add another test, describing our application. Let’s create the books controller, once again, we’re using the rspec generator
script/generate rspec_controller books
Go to the spec/controllers/books_controller_spec.rb and let’s write our test for the action “create”:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe BooksController do
it "should have an action that allow the creation of new books" do
controller.should respond_to(:create)
end
end
Ideally speaking, you will want to keep each “example” (the term rspec uses for each individual test) to the minimum assertions possible. That will make things more traceable later. Run the spec and watch it fail!
Now, time to write the minimum code necessary to make it pass, on the books controller:
def create
end
Run the specs again and see that its all green now.
Now we need to write another test, since clearly just having a method create on our books controller won’t do us any good. What’s missing is that the create method should receive the parameters to create a new book on the database and do it.
But this is a problematic point: if we write the test to actually persist our book on the database, we will be depending on book model logic again - which has already been tested somewhere else. This is not going to be a unit test at all, but a full stack one - which is bound to become slow pretty fast. Not to mention that if the controller tests depend on the model, and you screw up the model, the controller tests will also fail, totally ruining the traceability that your test suite should provide.
The solution for that is another widespread term: dependency injection. Any decent modern language have a whole bunch of frameworks for dependency injection a.k.a. inversion of control. What it means is that your implementation should be flexible enough to depend on the interface of its dependencies, not on their implementation. Putting it in simple terms, let’s say you’re programming an entity on C#, that depends on a repository for persistence, you should create an interface for this repository and have your entity provide some way of replacing the actual implementation with a fake one (mostly for the purpose of decoupled testing). But, since we’re working with ruby, and it has open classes, it is not even needed to bother with providing ways for dependency injection - just keep the concept in mind.
To properly make use of dependency injection, we need to setup fake implementation for the dependencies of our classes. Let’s look at our bookstore: the books controller depends on the book model, so we need to replace the model implementation on the controller with a fake one, that provides us with full control over it. What I’m talking about right now can be mainly categorized between two big concepts of unit testing: mocks and stubs. The difference between the two is very subtle on the first sight, so further reading about these two concepts is strongly advised. There are also some other important ones, like fakes and spies. But that isn’t needed for now, just try to get a grasp on their purpose in this practical example, continuing our bookstore:
describe BooksController, "creating a valid book" do
before(:each) do
@params_for_save = { "title" => "advanced joseki", "author" => "honinbou shusaku" }
@book = mock_model(Book)
Book.stub!(:new).and_return(@book)
end
def call_create
post :create, { :book => @params_for_save }
end
it "should instantiate a new book with the given parameters" do
Book.should_receive(:new).with(@params_for_save).and_return(@book)
call_create
end
end
One thing at a time, let’s look ath the “before each” method first:
- @params_for_save is just a helper to avoid repetition of the same hash of parameters over and over and over again
- @book represents a mocked instance of the book model - we’ll see more about this later on
- Book.stub! says that the Book (app/model/Book.rb) class will be replaced with this stub. When the method “new” is called, this stub will return our mocked book, instead of creating a new instance
What we gained from that? Let’s say the “new” (initialize) method of the original book model does something like checking the database for some condition, or calling an webservice, or whatever… by replacing the Book class implementation with this stub, we’re totally decoupling our controller test from any of that. These aspects of the Book model should have been already tested elsewhere (on the specific Book model spec) - so we can just assume that they will work and focus on what we are testing right now: the controller.
The “call_create” is just a helper method, once again to avoid repetition. What it does is just simulate a post (like a form would do) to the create method, passing the parameters like if it was being passed by the real form.
The example says that in the method we are testing (create), the constructor of the Book class shall be called, passing the parameters received on the post request - and when it is called, it will return our mocked book model. Running the specs now will fail, because our “create” method isn’t calling the constructor of the Book class, as the example describes it should.
To fix that we must instantiate a new book on the controller.
@book = Book.new params[:book]
This will make the specs run successfully! So, time to ellaborate on our examples:
describe BooksController, "creating a valid book" do
before(:each) do
@params_for_save = { "title" => "advanced joseki", "author" => "honinbou shusaku" }
@book = mock_model(Book, :save! => true)
Book.stub!(:new).and_return(@book)
end
def call_create
post :create, { :book => @params_for_save }
end
it "should instantiate a new book with the given parameters" do
Book.should_receive(:new).with(@params_for_save).and_return(@book)
call_create
end
it "should save the book on the database" do
@book.should_receive(:save!).and_return(true)
call_create
end
it "should notice the user about the success" do
call_create
flash.now[:notice].should match(/.*successfully.*/)
end
end
Things to notice:
- The mock_model call on the “before each” method has been slighly modified. That tells our mock that “save!” is a valid method, and when called it should return true. That means that when the controller calls it, during the test, it won’t actually save the book on the database, but just get a true return - just like the actuall Book would do if the save operation were successful
- There were added two new examples, which I think shouldn’t be a problem to understand. I’ve added two at once for the sake of eliminating the repetition on this article - while developing you should write one test (example, whatever you call it) and watch it fail, make it pass, then write another one and so on… Keep that in mind, because from now on I will quicken up the pace
Running the specs it shall fail - then let’s make it pass:
class BooksController < ApplicationController
def create
@book = Book.new params[:book]
@book.save!
flash[:notice] = "The book was saved successfully"
render :text => ""
end
end
Running the specs now shall give you an all green result.
To wrap it up, let’s see how we can add a error scenario to our controller test - like an invalid book. It will probably look something like this:
describe BooksController, "creating an invalid new book" do
before(:each) do
@params_for_save = { "title" => "the god delusion", "author" => "richard dawkins" }
Book.stub!(:new).and_return(@book = mock_model(Book, :save! => true))
end
def call_create
post :create, { :book => @params_for_save }
end
it "should instantiate a new book with the given parameters" do
Book.should_receive(:new).with(@params_for_save).and_return(@book)
call_create
end
it "should notice the user that the book is invalid" do
@book.should_receive(:save!).and_raise(StandardError.new("Error saving the book"))
call_create
end
it "should notice the user about the failure" do
@book.should_receive(:save!).and_raise(StandardError.new("Error saving the book"))
call_create
flash.now[:notice].should match(/.*error.*/)
end
end
Notice how I said that I was going to use an invalid book but used the exact same (business-wise VALID) parameters? That’s the wonder of mocking and stubbing - the controller test doesn’t need to and shouldn’t [must not! if you're purist enough] know what is a valid book at all. It just needs to know how the controller should react given that the book model showed that it is not valid. Once again: the test to check if a book is been properly validated have already been tested on the model level specs.
So, let’s make the tests pass - shall we?
class BooksController < ApplicationController
def create
@book = Book.new params[:book]
@book.save!
flash[:notice] = "The book was saved successfully"
render :text => ""
rescue
flash[:notice] = "There was an error saving the book"
render :text => ""
end
end
That will make all specs pass.
Notice that I didn’t mentioned anything about views - while rspec, not to mention the entire rails structure, gives you a pretty good support for testing them, I usually don’t do it on the unit test level [it probably is due to my lack of ability to do it properly, but the code simply tends to become really messy and not very readable]. So I try to leave it to cucumber.
As usual you can get the code used for this article on github. Please leave your impressions on the comments section.
Link deste post
25jul2009
por
macskeptic em
Agile,
BDD,
cmmi,
cucumber,
ddd,
english,
gedit,
gmate,
jruby,
kaizen,
kanban,
Linux,
netbeans,
os,
pmi,
rails,
ruby,
scrum,
Sem categoria,
so,
tdd,
test,
testing,
Ubuntu,
windows,
xp
This post is going to be the first of a series "Beyond The Buzzwords" of two (maybe three). This being a subject that has been getting some hype everywhere now doesn’t seem to be the kind of thing you would normally see here. We generally like to talk about the state of the art, and about things you can’t just google for - replicating knowledge like oh so many other blogs out there do, doesn’t particularly appeal to any of us.
But, since a great friend of mine asked for some very specific material about tests on rails, I decided to direct the effort to make something a little more presentable and put it here, so that anyone can benefit from it (and/or, most likely, criticize it).
Intro
Virtually any of us who ever worked with the whole PMI, CMMI overhead pestering the thinking process at each and every step might have found himself thinking something on the lines of: What the heck am I doing all this soon-to-be totally obsolete documentation for? How can I expect to know how the software will be written before starting to write it?
I know, the classic methodologies are not necessarily bad. They just tend to be really good for the mediocre minds, and really bad for the smart ones. Since there are a gazillion more of the first kind in this world we live on…
As an answer to that sort of overly defensive mentality, came the agile manifesto. Ever since, agile methodologies and buzzwords have been popping up furiously each and everyday. The vast majority of them coming from the well known Toyota’s Kaizen mentality.
At the very core of any reliable agile culture for software development [alongside many other things] lies the love for testing. Testing early, testing often, testing all the fucking time. Yes, love, you really got to eager for it every second of your development cycle.
It is often said that very good programmers are not seldom very bad at testing, and they simply couldn’t hate any more than they do. My former self is a pretty good example of that - a lightning fast programmer, a good understating of design patterns and practices but, testing? If you asked me a year ago, I would have said: that’s work for some monkey, not ME. 
But that was the old fashioned me, which was used to seeing manual testing, and thought actual automated testing was something that could only be seen on textbooks and academic projects. Once you get the grasp on the value and importance of testing, you just won’t be willing to ever do anything for serious without it. These days I find myself doing a test first approach for virtually anything that I truly wish to work - like the AI for a relatively simple board game I’m working with on my spare time right now.
Two of the most widespread terms for testing are BDD and TDD. I’ll be talking about these two on the two articles of this series - if the need arises I might even write a third to sum it up. This first article is all about BDD.
BDD stands for Behavior Driven Design (Development - nobody seems to know anymore which one is the right term, I use "Design" because I like it). This practice suggest a deeper relationship between the developers and the domain experts (your clients, most likely). The basic idea is to get both sides talking the same language so that they can, together, determine how the software that will be developed should behave.
To accomplish that, any framework that claims to be a tool for BDD shall provide a language that can be both understandable by only slightly technical people and programmatically viable to interpret on automated tests.
The ruby language holds one of the most remarkable of such frameworks out there: cucumber. For the next few paragraphs I will go through a very simple implementation of a bookstore (only the "add new book" functionality actually) using ruby on rails, from a BDD approach using cucumber.
If we were on an agile environment, most likely the development team would get a story to work on like this:
As the bookstore owner
I want to be able to register new titles to sell, including rating and synopsis
So that I can extend the list of products for sale without depending on manual intervention from the IT guys
After talking with the story owner, we decided that the relevant fields that a book entry should have are:
- Title
- Author
- Rating
- Synopsis
So now, let’s go to the fun part: code! [YATTA]
pre-reqs
To properly follow this series of articles you will most likely need the following (along with all their dependencies):
- Ruby
- Gem
- Rails
- Cucumber
- MySQL
- ZenTest (not for this article - but get it already!)
- Rspec
For the installation process of the aforementioned requirements on your platform of choice you are on your own though. A simple google search will most likely get you there - don’t forget to also install all their pre-reqs. You should be able to follow the steps below on any OS - I’m particularly using Ubuntu with GMate (a bunch of customizations for GEdit) as the text editor.
I would strongly advice against trying it on any Windows platform though. Don’t get me wrong, I love Microsoft products, but ruby just don’t get along well with Windows, not to mention there are not a single text editor nor IDE that feel good to work with it on the platform [but that's only my personal opinion - there are always those kids that love netbeans…]. If you must [you shouldn't], try to at least go with JRuby, it runs way smoother than the one click installer ruby on Windows.
1
First things first, creating the application and the databases (using mysql because I’m too lazy to install sqlite on my ubuntu VM):
rails --database=mysql bookstore
cd bookstore
rake db:create:all
rake db:migrate
2
Adding cucumber support:
script/generate cucumber
This should generate some basic infrastructure for your application.
3
Writing the first feature:
gedit features/register_new_books.feature &
Here is where the fun will begin. Remember our story? We’re going to translate it to cucumber:
First the feature overview, it is just for the sake of documenting, so you can write it however you feel its best:
Feature: Register new books
In order to make my bookstore expansible
As a bookstore owner
I want to be able to register new books
Now, we’re going to use a scenario outline, to define how this specific feature should behave when used:
Scenario Outline: User register a new book successfully
Given I am on the new book register page
Then I should see "Title"
And I should see "Author"
And I should see "Rating"
And I should see "Synopsis"
When I fill in "book[title]" with "<book_title>"
And I fill in "book[author]" with "<book_author>"
And I fill in "book[rating]" with "<book_rating>"
And I fill in "book[synopsis]" with "<book_synopsis>"
And I press "Save"
Then I should not see "<book_title>"
And I should not see "<book_author>"
And I should not see "<book_rating>"
And I should not see "<book_synopsis>"
And I should see "successfully"
And the book with the title "<book_title>" should have been saved on the database
One very important point to get here, is that if you use the constructs "I should see __", "I fill in __ with __", "I press __" - these, alongside with a couple more are predefined steps that webrat provides you out of the box. You can check them all on the webrat_steps.rb file.
Please note that the <> enclosed strings are placeholders, so that one can execute that set of steps for multiple examples of actual data. Now, we need to define those examples, in a mostly intuitive way:
Examples:
| book_title | book_rating | book_synopsis | book_author |
| the god delusion | 9 | too soft | richard dawkins |
| the antichrist | 9 | ubermesnch | friedrich nietzsche |
| holy bible | 0 | childish folklore | charlatans |
4
Giving the feature it’s first go, just type:
rake features
The output should be a colorful (if you’re on a decent OS/Command Line/Bash):
Feature: Register new books
In order to make my bookstore expansible
As a bookstore owner
I want to be able to register new books
Scenario Outline: User register a new book successfully
Given I am on the new book register page
Then I should see "Title"
And I should see "Author"
And I should see "Rating"
And I should see "Synopsis"
When I fill in "book[title]" with "<book_title>:"
And I fill in "book[author]" with "<book_author>:"
And I fill in "book[rating]" with "<book_rating>:"
And I fill in "book[synopsis]" with "<book_synopsis>:"
And I press "Save"
Then I should be on the new book register page
And I should see "Title"
And I should see "Rating"
And I should see "Author"
And I should see "Synopsis"
And I should not see "<book_title>"
And I should not see "<book_author>"
And I should not see "<book_rating>"
And I should not see "<book_synopsis>"
And I should see "successfully"
Examples:
| book_title | book_rating | book_synopsis | book_author |
CLUE | the god delusion | 9 | too soft | richard dawkins |
==> Can't find mapping from "the new book register page" to a path.
==> Now, go and add a mapping in
==> /home/macskeptic/workspace/rails/bookstore/features/support/paths.rb (RuntimeError)
==> /home/macskeptic/workspace/rails/bookstore/features/support/paths.rb:22:in `/^I am on (.+)$/'
CLUE features/register_new_books.feature:7:in `Given I am on the new book register page'
| the antichrist | 9 | ubermesnch | friedrich nietzsche |
Can't find mapping from "the new book register page" to a path.
Now, go and add a mapping in
/home/macskeptic/workspace/rails/bookstore/features/support/paths.rb (RuntimeError)
/home/macskeptic/workspace/rails/bookstore/features/support/paths.rb:22:in `/^I am on (.+)$/'
features/register_new_books.feature:7:in `Given I am on the new book register page'
| holy bible | 0 | childish folklore | charlatans |
Can't find mapping from "the new book register page" to a path.
Now, go and add a mapping in
/home/macskeptic/workspace/rails/bookstore/features/support/paths.rb (RuntimeError)
/home/macskeptic/workspace/rails/bookstore/features/support/paths.rb:22:in `/^I am on (.+)$/'
features/register_new_books.feature:7:in `Given I am on the new book register page'
3 scenarios (3 failed)
60 steps (3 failed, 57 skipped)
As you can see, it failed, since we got nothing on our application yet, just the feature specification. But it gave us a clue about what to do next, as I pointed above.
The phrase above is a very interesting concept - you write about the expected behavior before doing anything related to the code that will make it happen.
5
Since it said there is no "the new book register page" path, we’re going to add it, so that we solve the current problem and then we can try running the feature once again:
gedit features/support/paths.rb &
Here we will just define the missing path, adding something like this:
when /the new book register page/
'/books/new'
6
Running the feature again changes the error:
No route matches "/books/new" with {:method=>:get} (ActionController::RoutingError)
(eval):2:in `/^I am on (.+)$/'
features/register_new_books.feature:7:in `Given I am on the new book register page'
Once again, the error gives us a pretty nice hint about what to do next.
7
We need a route that matches books/new… that is a controller for books, with a method called new, so on to id:
gedit app/controllers/books_controller.rb &
Adding the method that should look something like this:
class BooksController < ApplicationController
def new
render :text => ""
end
end
I’ve chosen to render an empty text so that it won’t complain about there is no template yet.
8
Running the feature again changes the error:
expected the following element's content to include "Title":
(Spec::Expectations::ExpectationNotMetError)
features/register_new_books.feature:8:in `Then I should see "Title"'
3 scenarios (3 failed)
60 steps (3 failed, 54 skipped, 3 passed)
Could this error messages be any more helpful? I doubt it!
9
Since the last executing told us that "Title" could not be seen on the page, we will do the bare minimum code necessary to make it happy. Normally I would suggest that you do it for each term on the page (Title, Author, Synopsis and Rating), but for the sake of succinctness I will do the four on a single step (try not to do things like that if you’re beginning to adopt BDD just now, keep the steps SIMPLE, ridiculously SIMPLE)
Here:
gedit app/controllers/books_controller.rb &
We will change the rendering to:
render :text => "Title Synopsis Author Rating"
Remember: do only the bare minimum so that the current step can be successful.
10
Running the feature again changes the error:
Could not find field: "book[title]" (Webrat::NotFoundError)
(eval):2:in `/^I fill in "([^\"]*)" with "([^\"]*)"$/'
features/register_new_books.feature:12:in `When I fill in "book[title]" with "<book_title>"'
3 scenarios (3 failed)
60 steps (3 failed, 42 skipped, 15 passed)
We’re getting better, 5 steps passed already for each execution of the scenario outline (since we have 3 examples, 15 steps overall).
I guess you’re getting the grasp about what to do next, right? Hence and repeat…
11
Since the current step wants to fill some field on the page, we will most likely not find a way to implement it without creating a template.
We could create a pure HTML template, that would most likely be the best approach here, theoretically speaking - but once again, I have no intent of forcing a purist mentality over other people. You shall choose how much leeway you want to give to your steps, but keep in mind that you should always try your utmost to keep it to the minimum.
Since I’m planning to already use rails helpers to create a form for a model, I will need a model at this point. It is already too much implementation for a single step!
That’s when you can see how BDD per se falls short, lacking on dept on implementation heavy tasks. I mean, your client will most likely care that when he access a link, a page is shown with a given set of characteristics and a given behavior. But certainly he couldn’t care less about what sort of classes, controllers, models or whatever else techie stuff you need to make it happen.
Therefore, here’s a pretty good point to introduce the importance of TDD, so that’s going to be the main entry point of the next article. For now, please just put a bookmark here on your mind and bear with the inadequately huge implementation for this step.
Creating the view:
gedit app/views/books/new.html.erb &
Something like this:
<% form_for @book, :url => { :action => 'create' } do |f| -%>
<% field_set_tag 'New Book' do -%>
<p>
<%= f.label 'title'%><br />
<%= f.text_field 'title'%>
</p>
<p>
<%= f.label 'author'%><br />
<%= f.text_field 'author'%>
</p>
<p>
<%= f.label 'rating'%><br />
<%= f.text_field 'rating'%>
</p>
<p>
<%= f.label 'synopsis'%><br />
<%= f.text_area 'synopsis'%>
</p>
<p>
<%= f.submit 'Save' %>
</p>
<% end -%>
<% end -%>
Creating the model:
script/generate model book title:string author:string rating:decimal synopsis:text
rake db:migrate
Adapting the controller to use the view:
gedit app/controllers/books_controller.rb &
It will probably look like this now:
def new
@book = Book.new
end
12
Running the feature changes the error:
No action responded to create. Actions: new (ActionController::UnknownAction)
/usr/lib/ruby/1.8/benchmark.rb:308:in `realtime'
(eval):2:in `/^I press "([^\"]*)"$/'
features/register_new_books.feature:16:in `And I press "Save"'
3 scenarios (3 failed)
48 steps (3 failed, 18 skipped, 27 passed)
As you can see, the data input was successful - now we are stuck to the save button functionality, it redirects to the create action, which doesn’t exist as of now. So, you know what to do.
13
Adapting the controller:
gedit app/controllers/books_controller.rb &
With the simplest implementation possible to make the step pass:
def create
render :text => ""
end
Running the feature again should give an error about the word "successfully" not being present on the screen after clicking save.
So, let’s fix it:
def create
render :text => "successfully"
end
14
Running the feature again tells us that we don’t know yet what it means to the book to be on the database:
TODO (Cucumber::Pending)
features/register_new_books.feature:22:in `And the book with the title "<book_title>" should have been saved on the database'
3 scenarios (3 pending)
48 steps (3 pending, 45 passed)
15
So, we need to tell it, how to check if the book was saved on the database:
gedit features/step_definitions/book_steps.rb &
Here we will define a step, like this:
Then /the book with the title "([^\"]*)" should have been saved on the database/ do |book_title|
Book.find_by_title(book_title).should_not be_nil
end
The code above should be self explanatory by just reading it out loud, we will get into more details about this on the next post.
The regular expression will be matched against the steps on your feature description. Also, to make the assertion I’m using the rspec synthax - don’t worry about it right now, I’ll get into more details about it on the next article, since it is more directed to the TDD practice.
Here I shall give some more "do what I tell you, not what my shown code does" kind of talk: It is quite questionable to put such low level assertion on a feature. Your client might not even know what a database is. And the feature’s intent is to be readable and discussable by him too. So, there would probably be a lot better to go to the book listing page and check if your book is there as an assertion that it has been saved indeed.
Why I didn’t do that? Because I’m lazy and didn’t want to implement the listing page, and the few more steps needed to make the assertion - but that can be considered mandatory homework for you ;).
16
Now that cucumber knows what it means to check if the book has been saved to the database, when we run the feature again we get:
expected nil? to return false, got true (Spec::Expectations::ExpectationNotMetError)
features/register_new_books.feature:22:in `And the book with the title "<book_title>" should have been saved on the database'
3 scenarios (3 failed)
48 steps (3 failed, 45 passed)
That means, the book is not on the database.
17
So, what is left is to change the create action to actually do something:
gedit app/controllers/books_controller.rb &
Something like this will do the trick:
def create
Book.create(params[:book])
render :text => "successfully"
end
I know, you should check for errors and all that stuff - just don’t focus on the implementation I’m giving you (which is as succinct as possible, so that it fits better to be put inline here on codevil), but on the bigger picture concept behind it all.
18
Now, what is left is run the feature one last time, and rejoice:
Feature: Register new books
In order to make my bookstore expansible
As a bookstore owner
I want to be able to register new books
Scenario Outline: User register a new book successfully
Given I am on the new book register page
Then I should see "Title"
And I should see "Author"
And I should see "Rating"
And I should see "Synopsis"
When I fill in "book[title]" with ""
And I fill in "book[author]" with ""
And I fill in "book[rating]" with ""
And I fill in "book[synopsis]" with ""
And I press "Save"
Then I should not see "<book_title>"
And I should not see "<book_author>"
And I should not see "<book_rating>"
And I should not see "<book_synopsis>"
And I should see "successfully"
And the book with the title "<book_title>" should have been saved on the database
Examples:
| book_title | book_rating | book_synopsis | book_author |
| the god delusion | 9 | too soft | richard dawkins |
| the antichrist | 9 | ubermesnch | friedrich nietzsche |
| holy bible | 0 | childish folklore | charlatans |
3 scenarios (3 passed)
48 steps (48 passed)
Finally, all steps passing successfully.
END
That concludes this first article, I hope it has been of use to you guys beginning to do automated tests. As usual, you can download the full code used in this example at my github.
Please leave your impressions on the comments section.
Recommended [very short] reading: http://dannorth.net/introducing-bdd
Link deste post