TDD - Just do it!

Posted by caike on March 31, 2009

Este assunto foi abordado hoje em uma da listas de XP das quais participo e é algo que já venho pensando há algum tempo.

Mais de uma pessoa na lista concordou que um dos principais impedimentos na adoção de TDD seria a falta de apoio por parte da gerência e que métodos ágeis teriam de ser implantados top-down.  Eu discordo de ambas as afirmações.

Em situações onde gerentes não são técnicos (a grande maioria dos casos), acredito que as técnicas de engenharia de software tenham que vir de baixo pra cima. Você não deve esperar que seu gerente lhe incentive a testar para que você o faça. Provavelmente ele não está preocupado com isso. Ele não deve se preocupar com isso.

Um gerente facilitador não vai lhe dizer o que você deve fazer. Ele acredita que você e seu time sabem o que deve ser feito no que diz respeito ao código. Caso você não saiba, junte-se a alguém do seu time que saiba. Caso ninguém do seu time saiba, busque ajuda em outros times, livros, comunidade, etc.

Já um gerente controlador vai  dizer que o programa deve simplesmente funcionar. Simplesmente funcionando também não é o que procuramos e o software craftsmanship manifesto esclarece isto muito bem.

Acredito que como desenvolvedores somos os responsáveis por tomar a iniciativa de identificar as nossas próprias boas práticas e exercitá-las ao máximo para que nos acompanhem a todo instante.

Quando o número de bugs começar a diminuir, juntamente com o tempo de desenvolvimento de novas features, então vá em frente e fale para o seu gerente que isso é resultado de sua técnica de modelagem e de verificação através de seus testes unitários e de tudo de bom que eles trouxeram (baixo acomplamento, alta coesão, etc etc).

Provavelmente ele vai continuar não se preocupando com isso, mas vai gostar dos resultados.

Coisas como reuniões freqüentes com o cliente, time-box fixo, escopo variável e outras características mais relacionadas ao processo, realmente dependem de um mínimo de esforço de camadas gerenciais. A situação fica um pouco mais complicada em organizações de médio e grande portes e com características de produção fabril.

Mas, calma. Um passo de cada vez.

Estamos falando de boas práticas de engenharia de software, que não dependem de gerente algum. Dependem de você.

Cucumber and Wowspec

Posted by caike on March 09, 2009

It was back in September of last year that I first heard about RSpec. The possibility of bringing requirements in the form of User Stories straight to code amazed me and I quickly started playing around with its Story Runner.

In order to try out RSpec SR, I came up with a few lines of code and named it wowspec, in an attempt to describe and code some (very) small part of the behavior in World of Warcraft. One simple feature, that is.

But why World of Warcraft ?

Well, because it is fun and that is what programming should be all about.

To my surprise, a few months later during Rails Summit Latin America, David Chelimsky announced that the new Cucumber project would replace RSpec SR. Since then, I have been reading about Cucumber but never really tried out.

This week I decided to give Cucumber a try by rewriting my little wowspec example. I chose to keep the old name because I couldn´t come up with a better one related to Cucumber.

The project itself (check it out on my github) is really just an excuse to practice TDD (or BDD, if you wish), Ruby programming and also Cucumber. It is one single Cucumber feature, which describes a character being attacked in three different scenarios.

Scenario: Character in idle mode gets attacked
Given a character in idle mode
When he gets attacked with a sword
Then he should be in combat mode

Scenario: Character in combat mode gets attacked again
Given a character in combat mode
When he gets attacked with a sword
Then he should be in critical mode

Scenario: Character in idle mode gets fooled
Given a character in idle mode
When he gets attacked with a banana
Then he should be in idle mode

I have been using this wowspec example for introducing agile best practices to people new to unit testing and TDD, either using Java (JUnit or JBehave) or Ruby and the results have been great!

Leaving the “enterprisey” world every now and then and looking for inspiration from other sources brings joy back to programming, I believe. I strongly encourage everyone to do the same. Games, sports, nature, space, sci-fi, whatever.

Come up with something you enjoy and bring it to life through code. You will be excercising your coding skills, uncovering your own best practices and enhancing your software craftsmanship.

Software Craftsmanship Manifesto 2

Posted by caike on March 07, 2009

It’s not only about making something work. Just coding the “simplest thing that could possibly work” is not how your final product should look like. That’s not code that should go into production and I believe it shouldn’t bother your continuous integration process either.

It’s not about just killing bugs and being able to change requirements no matter what. It’s about doing it the best way by producing easily maintainable legagy code.

It’s not about getting together everyday for 10 minutes while standing up and calling it “Daily Scrum”, or “Dailly Meeting”. It’s about exchanging ideas and sharing your recently gained experience, as well as learning from others.

It’s not about having someone who you can call “client”. Having a client is just one of the many steps towards agile development (and a very important step). It’s more than that. You need a partnership with someone that seeks business value and is willing to invest in it.

It’s not about getting it done, it’s about doing it right.

This is the Software Craftsmanship Manifesto:

As aspiring Software Craftsmen we are raising the bar of professional software development by practicing it and helping others learn the craft. Through this work we have come to value:

Not only working software, but also well-crafted software.

Not only responding to change, but also steadily adding value.

Not only individuals and interactions, but also a community of professionals.

Not only customer collaboration, but also productive partnerships.

That is, in pursuit of the items on the left we have found the items on the right to be indispensable.

Test Driven Development in Rails - Part I (Models)

Posted by caike on March 04, 2009

The following is part one of a series of examples of a simple feature developed using Ruby on Rails. The intent of this post is to show how the test code can (and should, as much as possible) be written before the actual implementation code, which is an agile practice known as Test Driven Development.

I expect this series of examples to show how writing tests is actually a design activity rather than simply code verification. Each new test runs along with all the previous tests. This automation helps developers by giving them constant feedback.

The Steps

According to Kent Beck, the rythm of Test Driven Development can be summed up as follows:

1. Quickly add a test.

2. Run all tests and see the new one fail.

3. Make a little change.

4. Run all tests and see them all succeed.

5. Refactor to remove duplication.

The Tools

There are many testing framework options to choose from in the Ruby world (RSpec, Cucumber, Shoulda, etc.) but in this example I’m going to keep it plain and simple using just Test::Unit for unit testing the models and ActionController for testing controllers as well as testing routes.

After writing each test and ensuring that it fails, we will write the simplest thing that could possibly work. At first it may look ugly and not “the Rails way” of doing things, but we will later enhance its quality using another agile technique known as refactoring.

Rails out-of-the-box expects you to have a database setup and creates default fixtures for each of your models. Although there is a way to get around that, I’ll leave it alone for now.

The problem

Our client wants us to develop an e-commerce app for his musical instrument shop. From our meetings, we agreed that users should be able to buy instruments online. He said that for an instrument to be considered valid it must belong to a category, have a description and have a price. If either of those conditions is not true, then the instrument is not valid.

Making it work

Notice how the meeting revealed a few keywords and concepts to be used in the construction of our domain model and its relationships (instrument, description, category, price, belongs to).

We start off creating our application:

$ rails music_shop
$ cd music_shop

and creating our first model:

$ ./script/generate model instrument

The output shows that a bunch of files were created. At this moment, only three files are important for us: instrument migration file, model and unit test.

Our client told us he wants an instrument with description, price and category, therefore they will be the initial attributes for instrument.

Add the following snippet to some_number_create_instruments.rb located in db/migration.

create_table :instruments do |t|
     t.string       :description
     t.decimal      :price, :precision => 8, :scale => 2
     t.references   :category

     t.timestamps
end

This migration file is going to build the instruments database table using the adapter defined in database.yml, located in the config folder. Unless defined otherwise, Rails uses SQLite as dbms and I suggest that you keep it that way until deployment (or even until SQLite becomes a bottleneck for you app).

Run your migration:

$ rake db:migrate

The database table has been created for you and you don’t need to worry about it anymore.

Now we should focus on the business requirements. Open test/unit/instrument_test.rb and add the following snippet that relates to what the client said about a valid instrument.

  def setup
    @valid_params = {:description => "gibson sg",
                     :price => 100.00, 
                     :category => Category.new}
   end

  test "not valid without description" do
    guitar = Instrument.new(@valid_params.merge(:description => nil))
    assert !guitar.valid?
    assert guitar.errors.on(:description)
  end

 

Let’s run our first test

$ rake test:units

and watch it fail!

1 tests, 0 assertions, 0 failures, 1 errors

It was expected to fail since we have not implemented any validation yet. Actually it has not failed, but spit an error instead:

1) Error:
test_not_valid_without_description(InstrumentTest):
NameError: uninitialized constant InstrumentTest::Category

The error says there is no Category class defined. So lets generate our missing model, run the created migration and then run the test again.

$ ./script/generate model category
$ rake db:migrate
$ rake test:units

No complaining about missing class, but it says there is no category attribute for our Instrument model.

1) Error:
test_not_valid_without_description(InstrumentTest):
ActiveRecord::UnknownAttributeError: unknown attribute: category

Indeed, we haven’t coded anything that says that an instrument belongs to a category. So let’s do that in our Instrument model:

class Instrument < ActiveRecord::Base
  belongs_to :category
end

Run the tests again and notice how the errors are gone. Now there is just a failing test that needs to get out of our way.

2 tests, 2 assertions, 1 failures, 0 errors

Notice how the test code is guiding us through design and it’s telling us what needs to be done, like in a two-way conversation.

The process of starting a feature with failing tests and coding towards making the tests pass is known as “red green refactor”, resembling the red and green bars present in previous unit test framework outputs (JUnit being the most famous). Taking baby steps is a safe way to measure the impact of you actions in the code.

Only one line is needed to make the failure go away. In the app/models/instrument.rb add the following:

class Instrument < ActiveRecord::Base
  belongs_to :category
  validates_presence_of :description
end

Run the test again and watch the failure go away.

1 tests, 1 assertions, 0 failures, 0 errors

I believe that as you gain proficiency with test driving your code, you can go ahead and write a couple of failing tests before going into implementation code - as long as they are related to the same context of the unit under test.

Moving on with our tests, let’s add two more:

 test "not valid without price" do
    guitar = Instrument.new(@valid_params.merge(:price => nil))
    assert !guitar.valid?
    assert guitar.errors.on(:price)
  end

  test "not valid without category" do
    guitar = Instrument.new(@valid_params.merge(:category => nil))
    assert !guitar.valid?
    assert guitar.errors.on(:category_id)
  end

Run it again with rake and watch it fail:

3 tests, 3 assertions, 2 failures, 0 errors

Now only the barely essential to make the above tests pass is added, and the complete instrument model looks like the following:

class Instrument < ActiveRecord::Base
  belongs_to :category                      

  validates_presence_of :description, :price
  validate :has_a_category

  def has_a_category
    if category.nil? && category_id.nil?
      errors.add(:category_id, "must have a category")
    end 
  end
end

Run the tests again and they should all pass

5 tests, 5 assertions, 0 failures, 0 errors

Conclusion

I hope the previous steps showed some of the benefits of test driven development, which is, in my humble opinion, one of the most important agile practices.

It should be taken as a design practice rather than pure verification. Running automated tests keeps the developers in control of the application and makes code easier to maintain and evolve.