RSpec and Watir to test web applications

Testing is cool

Software testing

Software testing is an investigation conducted to provide stakeholders with information about the quality of the product or service under test. Software testing can also provide an objective, independent view of the software to allow the business to appreciate and understand the risks of software implementation. Test techniques include, but are not limited to, the process of executing a program or application with the intent of finding software bugs (errors or other defects) – Wikipedia

The main intend of this post is, introduce you to UI tests over some ruby toys. In fact you could create an entire project (new) in ruby just to test your legacy web project. It’s cool, you can learn new language and work for the improvement of your legacy product. If you are totally new for ruby maybe a ruby overview can help you. (or might confuse you more)

Installing ruby, watir and rspec

Instead of installing the ruby directly, we are going to install the RVM (Ruby Version Manager) to then install any ruby we need. The steps described here were made on Ubuntu 11.04. On your terminal do the magic to install RVM.

bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
echo ‘[[ -s “$HOME/.rvm/scripts/rvm” ]] && . “$HOME/.rvm/scripts/rvm” # Load RVM function’ >> ~/.bash_profile
source .bash_profile

And from now on, your life will be better on ruby interpreters versions. Let’s install the ruby 1.9.2. (terminal again)

rvm install 1.9.2

And if we want to see the rubies installed on our machine?

rvm list

And now, how can we chose one ruby to work on the terminal session?

rvm use 1.9.2

For the test purpose we will use Watir and RSpec, great tools for testing, make fun with BDD and the best thing is install them it’s very easy.

gem install watir-webdriver
gem install rspec

Hands-on

Since we have all things installed, we can move for the example. The feature I want to test is the search system of  Amazon. Being more precise, I want to search for ‘Brazil’ and see if the ‘Brazil on the Rise’ is within the results as I want to be sure when I search for ‘semnocao‘ the Amazon doesn’t provide any result. Now, we can write the spec.

require 'amazon_page'

describe AmazonPage do
 before(:each) do
   @page = AmazonPage.new
 end
 after(:each) do
   @page.close
 end
 it "should show 'Brazil on the Rise' when I query for [Brazil]" do
  @page.query 'Brazil'
  @page.has_text('Brazil on the Rise').should == true
 end
 it "should bring no result when I search for [semnocao]" do
  @page.query 'semnocao'
  @page.results_count.should == 0
 end
end

The specification is very simple, it will create a page before each test calling and close the page after each test calling. There is only two tests: test when you search for Brazil  and  when you search for semnocao. We will design the tests using page object pattern. The class bellow is the page which represents the Amazon page and all testable behaviors should be inside of it.

require 'watir-webdriver'

class AmazonPage
 def initialize
  @page = Watir::Browser::new :firefox
  @page.goto 'http://www.amazon.com'
 end
 def close
  @page.quit
 end
 def query(parameter)
  @page.text_field(:id=>'twotabsearchtextbox').set parameter
  @page.send_keys :enter
 end
 def has_text(text)
  @page.text.include? text
 end
 def results_count
    if @page.text.include? 'did not match'
     0
    else
     @page.div(:id=>'resultCount').text.split(' ')[5].gsub(',','').to_i
    end
 end
end

To run this you just need to type on your terminal.

rspec spec/

The final code can be downloaded or viewed at github.

Additions

  • We could improve our story readibility  with Cucumber.
  • We could send the browser execution to an Xvfb server. (A.K.A. running headless) The browser pops up really bothers me.
  • We could integrate it with our CI.
  • We could design a base Page class for provide common operations as mixin or something

ps: the post was very inspired by KK post and Saush.

Advertisements

Acceptance testing on Fitnesse using slim test system

What is Fitnesse?

  • It’s a software development collaboration tool.
  • It’s a software testing tool.
  • It’s a wiki.
  • It’s a web server.

This tool provides a way for the BA’s and/or customers write their acceptance testing on wikis and better than this, they can run theirs tests and see if it fails or pass right on the page. Install Fitnesse it’s very simple, you just download and execute. The fitnesse architecture provides two types of test system: slim and the well-known fit. For this tutorial I’ll use the slim.

The hands-on

Scenario: Given that I have the input1 and input2 Then the output Should Be input1 plus space input2. So let’s express the acceptance testing of this feature (or behavior if you want). In Fitnesse we can express this using the decision table, others examples of places to write tests are query table, script table, library table and etc.

|it should print guaqmire|
|input1|input2|output?|
|you|are|you are|
|family|guy|family guy|
|tests|enough|tests enough|

Explaining the table, the first line is the name of the fixture, the second line contains the inputs and outputs names and from the third line on it’s filled with testing data. The output table should look like this:
Let’s setup the wiki-page to it became runnable, edit the page and put these parameters on the page.

!define TEST_SYSTEM {slim}
!define TEST_RUNNER {C:\fit\slim\rubyslim\lib\run_ruby_slim.rb}
!define PATH_SEPARATOR { -I }
!define COMMAND_PATTERN {ruby -I %p %m}
!path C:\fit\slim\rubyslim\lib\
!path C:\fit\ruby\prj\

The first line is setting TEST_SYSTEM to configure fitnesse to use the slim protocol instead of default fit. As we will use slim and ruby, we’ll use the rubyslim library. The second line is setting the TEST_RUNNER to use the ruby slim. Third line defines the PATH_SEPARATOR, used by the COMMAND_PATTERN to separate paths. The fourth line is configuring the COMMAND_PATTERN that will execute the test itself, the two parameters %p (receives all the paths from the page and its ancestors) and %m (the fixture itself) are used to correctly perform the test. The lines with path just informs to fitnesse where it can found the libraries and the runtime files.

And now you can run, is it fails? Good, now let’s programming it in ruby, if you are a newbie ruby as me, your code might be something like this.

module Fixtures
  class ItShouldPrintGuaqmire
   def set_input1 input1
    @input1 = input1
   end
   def set_input2 input2
    @input2 = input2
   end
   def output
    "#{@input1} #{@input2}"
   end
  end
end

Ohh it continues to fail, shame on me. As you can see at the code, I put the fixture inside a module called Fixture, so we need to inform the fitness what module/package is my fixture and we can do that by a table.

|Import|
|Fixtures|

This special table only  configures where is the fixtures. Now let’s see the entire code for fitnesse wiki.

!define TEST_SYSTEM {slim}
!define TEST_RUNNER {C:\fit\slim\rubyslim\lib\run_ruby_slim.rb}
!define PATH_SEPARATOR { -I }
!define COMMAND_PATTERN {ruby -I %p %m}
!path C:\fit\slim\rubyslim\lib\
!path C:\fit\ruby\prj\

|Import|
|Fixtures|

|it should print guaqmire|
|input1|input2|output?|
|you|are|you are|
|family|guy|family guy|
|tests|enough|tests enough|

Running the tests should show

Bonus round – Fitnesse using slim protocol in Java

In fact to make it runnable in Java it’s easier, you don’t need any TEST_RUNNER or COMMAND_PATTERN in your wiki page, since Java it’s default for fitnesse and your final wiki should look like this:

!define TEST_SYSTEM {slim}
!path C:\fit\java\prj\fit-slim-java.jar

|Import|
|br.com.leandromoreira.fixtures|

|it should print guaqmire|
|input1|input2|output?|
|you|are|you are|
|family|guy|family guy|
|tests|enough|tests enough|

And your Java code can be something like this:

package br.com.leandromoreira.fixtures;

public class ItShouldPrintGuaqmire{
  private String input1;
  private String input2;
  public void setInput1(final String i1){
   input1 = i1;
  }
  public void setInput2(final String i2){
   input2 = i2;
  }
  public String output(){
   return input1 + " " + input2;
  }
}

Real world

Usually the real world projects requires a lots of libraries on path, setup pages and more than just decision table to write tests. For instance, you can see that use fitnesse with slim seems more portable , less coupled with runtime and easier to implement too. In the real world you also create wiki for the project and suite test page for stories and organize all your imports and configs on project level, when you are composing a wiki on fitnesse you can take advantage of the fact that all the pages extend the configs from theirs ancestors, so you can have a better project wiki and managable test suite pages.

Update – Issues with Ruby 1.9.x

If you are trying to use the ruby 1.9.x you will have some issues the first one is: require ‘jcode’ issue, I tried to solve it but then it started to show another error list_deserializer.rb:1:in `<‘: comparison of String with Float failed (ArgumentError)’.  Since I’m not (still) a ruby guy I don’t know how to fix it.

ThoughtWorks Brazilian hiring process

The hiring process

My intend here is to explain my personal view of the hiring process (which I was submitted) of Brazilian ThoughtWorks. In fact the ThoughtWorks hiring process is already explained, see a brief view of it:

“Hiring is our signature process, so as you might expect, we’ve thought hard about how we access your suitability for a career with us. We believe we’ve created a process that is fun, that shows you what being a ThoughtWorker is all about and challenges your abilities. Many hiring processes consist of a couple of interviews and perhaps a chance to meet your new boss. We reject that. We want to find out what work environment suits you, what you value and how you do your job, so we don’t just sit and ask you questions. We get you to show us what you can do.”

The steps (Dev role)

The TW talent scout sent me a message telling me about the open positions at TW (Porto Alegre) and then I answer her asking how to apply, she informed everything I need to do. Then I’ve applied to dev position (03/20/2011) and the First Step: a informal phone interview, that first interview it’s very easy and weightless 🙂 just to know you a little bit. The Informal Phone Interview is designed to help tw get to know each other beyond CVs and web pages.

Second Step was the code submission: they will send you two problems, you must choose only one, to solve. On this part they are trying to access a number of things, these include the design aspect of your solution, but mostly we are looking for good coding practices and your object-oriented programming skills. Good tip here is: use your primary programming language (I do love and want to learn ruby but Java was my main tool that moment).

Then if you have passed on this phase you will be on Third Step: the tech phone interview that consists of one ThoughtWorker interviewing you more technically, in my case Rodrigo Wolschick (A.K.A. Patrola) did the interview and despite his nickname he was fine with me.

So after that you will be on Fourth Step: Office Interview (here the real fun will start), they ask me to schedule two days to be at office. At the office you will pass through several interviews (culture values, programming pairing…) and logical assessments, I can tell you it’s very tiring BUT IT’S SURPRISINGLY FUN. After this long process I started to work at June 21.

PS: I should write this before I’ve posted ThoughtWorks POA.

Ohh, one last note we’re hiring, so if you are interested :

Open positions at ThoughtWorks Brazil

TDD in practice – developing a game

Test-Driven Development

On this post I’ll try to explain on practise some concepts and techniques behind the TDD and I’ll do it by example.

Scenario


Product: A role-playing game titled as Breath of Fantasy

Story: The battle scheme – Create the battle scheme for the game.

Description:
The battle will be based on turns, each time a character attacks and the other receives the damage. The character has energy points and power points. These two properties are integers numbers. For example, if the hero(energy points: 60, power points:45) attacks a enemy(energy points: 60, power points:45) the enemy will have the energy decreased some points. The damage received, decreased energy points, by enemy depends on luck factor. Luck factor is a random number from 0 to 100 that is given for each attack turn.

There is four kinds of attacks based on your luck factor:

  1. When the luck is 0-3 then the attack is Miss -> doesn’t cause any damage;
  2. When the luck is 4-70 then the attack is Normal -> causes 1/3 of your power points in damage;
  3. When the luck is 71-96 then the attack is Lucky -> causes 1/3 of your power points plus twenty percent of this 1/3 in damage;
  4. When the luck is 97-100 then the attack is Critical -> causes the double of normal attack.

STEP BY STEP

Write a tests to given story, the first one should validate when a character attacks another with Miss attack and see if the damage caused is 0.

@Test
public void validateMissAttack(){
   UnitCharacter hero = new UnitCharacter("hero",60,45);
   UnitCharacter enemy = new UnitCharacter("enemy",60,45);
   hero.attack(enemy);
   Assert.assertEquals(enemy.getEnergyPoints(), 60);
}

You still don’t have the class UnitCharacter, so you can create it now. You should create it just enough to compile and run the test.

public class UnitCharacter {
 public UnitCharacter(String name, int energy, int power) {
 }
 public void attack(UnitCharacter other) {
 }
 public int getEnergyPoints() {
  return 0;
 }
}

When you try to run the test it will fail; PERFECT this is what we want! Now move to next, write code to that test pass. The most obvious /simple solution should be used:

public int getEnergyPoints() {
  return 60;
}

Run the test again, test pass; PERFECT this is what we want! Now move to next, refactor the code. The constructor of UnitCharacter lets spaces for misinterpretations for example use the second argument as power point instead of energy. To solve this possible problem we could use tiny types.

public UnitCharacter(String name, Energy energy, Power power) {
}

public class Energy {
 public Energy(int energy) {
 }
 public int getEnergyPoints() {
  return 60;
 }
}

public class Power {
 public Power(int power) {
 }
}

Looking the new code, we can imagine that there will be a little change in UnitCharacter. The method getEnergyPoints() will just delegate the calling to Energy. Now we’ve refactored all it just run the test again and see if it passes. Now we can move next: create the next tests.

Given: A hero(energy points: 60, power points:45) and a enemy(energy points: 60, power points:45)

  • Test: when the hero attacks the enemy with Normal attack and see if the damage caused is 15.
  • Test: when the hero attacks the enemy with Lucky attack and see if the damage caused is 18.
  • Test: when the hero attacks the enemy with Critical attack and see if the damage caused is 30.
    @Test
    public void validateNormalAttack(){
        UnitCharacter hero = new UnitCharacter("hero",new Energy(60),new Power(45));
        UnitCharacter enemy = new UnitCharacter("enemy",new Energy(60),new Power(45));
        hero.attack(enemy);
        Assert.assertEquals(enemy.getEnergyPoints(), 45);
    }
    @Test
    public void validateLuckyAttack(){
        UnitCharacter hero = new UnitCharacter("hero",new Energy(60),new Power(45));
        UnitCharacter enemy = new UnitCharacter("enemy",new Energy(60),new Power(45));
        hero.attack(enemy);
        Assert.assertEquals(enemy.getEnergyPoints(), 42);
    }
    @Test
    public void validateCriticalAttack(){
        UnitCharacter hero = new UnitCharacter("hero",new Energy(60),new Power(45));
        UnitCharacter enemy = new UnitCharacter("enemy",new Energy(60),new Power(45));
        hero.attack(enemy);
        Assert.assertEquals(enemy.getEnergyPoints(), 30);
    }

When you try to run all these tests they fail; PERFECT this is what we want! Now move to next, code to these tests pass. I’ll focus on validateNormalAttack method test to make it pass.

public class Power {
    private int power;
    public Power(int power) {
        this.power = power;
    }
    public int getPowerAttack() {
        return power/3;
    }
}

public class Energy {
    private int energy;
    public Energy(int energy){
        this.energy = energy;
    }
    public int getEnergyPoints(){
        return energy;
    }
    public void decrease(int attack) {
        energy -= attack;
    }
}

public class UnitCharacter {
    private final Energy energy;
    private final Power power;
    public UnitCharacter(final String name,final Energy energy,final Power power) {
        this.energy = energy;
        this.power = power;
    }
    public void attack(final UnitCharacter other) {
        other.energy.decrease(power.getPowerAttack());
    }

    public int getEnergyPoints() {
        return energy.getEnergyPoints();
    }
}

When you rerun the tests the validateNormalAttack pass but the test validateMissAttack fails! The test suite help us to know it and now we can code to fix this fail! One important concept is the luck factor which influences the type of attack, let’s try to design the luck to the attack.

public interface Luck {
    double nextAttackLuckFactor();
}

public class LuckAttack implements Luck {
    private final Random random = new Random();
    private final static double MISS = 0;
    private final static double NORMAL = 1;
    private final static double LUCKY = 1.2;
    private final static double CRITICAL = 2;
    @Override
    public double nextAttackLuckFactor(){
        int randomFactor = random.nextInt(101);
        if (randomFactor > 0 & randomFactor <=3){
            return MISS;
        } else if (randomFactor > 3 & randomFactor <= 70){
            return NORMAL;
        } else if (randomFactor > 70 & randomFactor <= 96){
            return LUCKY;
        } else {
            return CRITICAL;
        }
    }
}

public class Power {
    private int power;
    private Luck luck;
    public Power(int power) {
        this.power = power;
        this.luck = new LuckAttack();
    }
    public int getPowerAttack() {
        return (int) ((power / 3) * luck.nextAttackLuckFactor());
    }
}

Now if we rerun the tests sometimes one or two passes sometimes all of them fails. Why? The luck is a random number then is unpredictable. Now we can use mocks / stubs objects (there is a tech discussion about the differences between mock and stub) to simulate the desired luck factor.

public class Power {
    private int power;
    private Luck luck;
    public Power(int power,Luck luck) {
        this.power = power;
        this.luck = luck;
    }
    public int getPowerAttack() {
        return (int) ((power / 3) * luck.nextAttackLuckFactor());
    }
}

public class TestAttack {
    @Test
    public void validateMissAttack(){
        Luck missLuck =  new Luck() {
            @Override
            public double nextAttackLuckFactor() {
                return 0;
            }
        };
        UnitCharacter hero = new UnitCharacter("hero",new Energy(60),new Power(45,missLuck));
        UnitCharacter enemy = new UnitCharacter("enemy",new Energy(60),new Power(45,missLuck));
        hero.attack(enemy);
        Assert.assertEquals(enemy.getEnergyPoints(), 60);
    }
    @Test
    public void validateNormalAttack(){
        Luck normalLuck =  new Luck() {
            @Override
            public double nextAttackLuckFactor() {
                return 1;
            }
        };
        UnitCharacter hero = new UnitCharacter("hero",new Energy(60),new Power(45,normalLuck));
        UnitCharacter enemy = new UnitCharacter("enemy",new Energy(60),new Power(45,normalLuck));
        hero.attack(enemy);
        Assert.assertEquals(enemy.getEnergyPoints(), 45);
    }
    @Test
    public void validateLuckyAttack(){
        Luck luckyLuck =  new Luck() {
            @Override
            public double nextAttackLuckFactor() {
                return 1.2;
            }
        };
        UnitCharacter hero = new UnitCharacter("hero",new Energy(60),new Power(45,luckyLuck));
        UnitCharacter enemy = new UnitCharacter("enemy",new Energy(60),new Power(45,luckyLuck));
        hero.attack(enemy);
        Assert.assertEquals(enemy.getEnergyPoints(), 42);
    }
    @Test
    public void validateCriticalAttack(){
        Luck criticalLuck =  new Luck() {
            @Override
            public double nextAttackLuckFactor() {
                return 2;
            }
        };
        UnitCharacter hero = new UnitCharacter("hero",new Energy(60),new Power(45,criticalLuck));
        UnitCharacter enemy = new UnitCharacter("enemy",new Energy(60),new Power(45,criticalLuck));
        hero.attack(enemy);
        Assert.assertEquals(enemy.getEnergyPoints(), 30);
    }
}

This refactoring made the Power uncoupled of the concrete implementation of Luck this has drive our design to a better state. We could use a mock framework as Mockito, JMock and others to simulate the “falses” results. Now try to rerun the tests. All pass great. Next step refactor the test class there is too much duplicated code. Always try to be DRY

IS JUST IT? NOW THE TESTS ARE OVER…?!

No! for each new class we’ve created we should create unit tests for them. For example:

  • what if I create a Energy or Power passing negative number to it?
  • what if I create a UnitCharacter with null Energy?
  • what if I create a UnitCharacter with energy points equals to 97 and power points equals to 51 how the round will work?

You need to to feel safe and confidence about you code. There is some tools for analysing the coverage of your unit tests (like Cobertura) over the project.

THE BENEFITS

The design was entirely modelled by TDD. Now you have a solid suite of tests and when you would do the design of the spell and item usage you can run this tests again and see if they fails. TDD not only give us safety it also lead us to the appropriate design, we don’t spent too much time thinking in the future, we solve the problem of that story and we prove our solution.

IS THAT THE BEST SOLUTION TO THIS PROBLEM

No! You probably has a better one, this is one of the possible.