Selenium Tutorial: Selenium Remote Control

7. Selenium Remote Control (RC)

7.1. Overview

  • While Selenium IDE may seem a productive and efficient tool for writing test-cases, it lacks many essential features of a testing tool:

    • Conditional statements
    • Loops
    • Logging
    • Exception handling
    • Reporting
    • Test fixtures and data-driven tests
    • Test dependencies
    • Taking screenshots
  • Selenium RC is the answer to a more powerful test-suite for your applications.
  • It follows a client/server model allowing client libraries to execute tests on a browser controlled by the server.

Figure 7. Overview of Selenium RC

images/selenium_rc_overview.png

7.2. Selenium Server

  • Selenium server is the program that drives the browser
  • It embeds Selenium Core framework and injects it into the browser
  • It communicates with the running test client and drives the browser
  • Client tests sends commands that the server interpretes in order to drive the browser
  • The server sends back results to the client
  • Client and server communicates via HTTP GETs and POSTs so you can easily plug into
  • Server is configurable at startup via command-line options. use java -jar selenium-server.jar -h to see the list of options

7.3. Client libraries

  • Client libraries provides the API to program tests and execute them on the server
  • Each implementation provides access to all Selenium commands
  • Supported API implementation exists in:

    • Java (also accessible via Groovy)
    • .Net
    • PHP
    • Python
    • Perl
    • Ruby

7.4. Lab 3 : Installation of Selenium RC

7.4.1. Prerequisites

  • A JVM installed on the system
  • A Ruby distribution with the rspec and selenium-client gems installed if
  • you’re working
  • The Selenium RC archive

7.4.2. Download The Tested Application

  • In the folder of your choice, execute the following command:

    git clone http://github.com/wolframarnold/selenium-test-app.git

  • Go to the selenium-test-app folder and run the following:

    • cp ./vendor/plugins/active_scaffold/test/mock_app/config/database.yml ./config/
    • rake db:migrate
  • Now you should be able to lauch the application with the script server available in the scripts folder:

    ./scripts/server

  • The application should be available at http://localhost:3000

7.4.3. Running The RC Server

  • Unzip the Selenium RC archive somewhere on your system
  • Go into the selenium-rc, then the selenium-server folder
  • Open a terminal in this folder and enter the command java -jar selenium-server.jar
  • If there’s no error messages, your Selenium RC server should be running now.

7.4.4. Running A Test (Java)

  • Create a new Java project in Eclipse or your Java IDE
  • Add the selenium-java-client-driver.jar jar to the project’s classpath
  • Add a new JUnit test-case to the project. When asked, select Junit 4
  • You should now have an empty .java file
  • Open the lab2 test-case on Selenium IDE
  • On Selenium IDE, open the Options menu then Format… and click Java (Junit)
  • Copy/paste the contents in the JUnit file on Eclipse
  • Change the package name at your will
  • You may have to fix the imports

    • Click on the following icon and choose the most appropriate option to correct the file
    • You may also press Ctrl + Shift + O to let Eclipse do it automatically
  • At this point you shouldn’t have any more errors and you are ready to run the test
  • Selenium RC will open a couple of Firefox windows and run the test

7.4.5. Running A Test (Ruby)

  • Open the lab2 test-case on Selenium IDE and export it as Ruby (rspec)
  • A little workaround is needed, open the exported file:

    • On line 47, where you should have: /^Are you sure you want to delete <suggestion_name>[\s\S]$/ =~ page.get_confirmation.should be_true
    • Change it to: page.get_confirmation.should match(/^Are you sure you want to delete <suggestion_name>[\s\S]$/)
  • Run the test with spec <filename>.rb
  • Selenium RC should launch a couple of new browser windows and run the test

7.5. Junit

  • Junit is the reference unit-testing framework for Java
  • The framework allows you to test your applications using specific classes containing the logic performing actions on the tested classes and checking the results
  • Eclipse provides support for Junit, so we’ll be using that
  • JUnit 3 uses named methods to detect set-up and test methods
  • JUnit 3 cheat sheet:

    import static org.junit.Assert.assertEquals;
    
    public class AdditionTest { //1
    
      DeepThought dt;
    
      public void setUp() { //2
        dt = new DeepThought();
        dt.initialize();
      }
    
      public void tearDown() { //3
        dt.closeQASession();
        saveResult();
      }
    
      public void testDeepThought() { //4
        int answer = dt.ask("What is the meaning of life, the universe and everything?");
        assertEquals(42, answer); //5
      }
    
    }

    1

    The tests are embedded within a classic JavaBean

    2

    The setUp() method will be executed once, before any other method

    3

    tearDown() method is executed after the last test method

    4

    Name your tests methods following the testSomething() pattern. They will be executed in the order of declaration

    5

    Junit uses assert... methods to provide verification of values.
[Note]Note

In our tests, we’ll use verify... methods provided by the selenium driver instead

7.6. RSpec

  • RSpec is a Behaviour Driven Development framework for Ruby
  • It provides a framework that tests your apps according to defined behaviors
  • In Behavior Driven Development (BDD), you first define the behaviors of your app according to the client’s need, then your write the tests that validate these behaviors and then you start to develop your application.
  • It is Agile-oriented
  • Behaviors are defined in a spec file containing set-up methods as well as methods providing tests for a given behavior
  • You run tests with the spec command in a terminal
  • RSpec cheat sheet:

    require 'thing' 1
    
    describe Thing do 2
    
    before do 3
     @thing = Thing.new
    end
    
    it "should find the Answer to the Ultimate Question of Life, the Universe, and Everything" do 4
      @thing.answer.should == 42
    end
    
    it "should enforce the Answer to the Ultimate Question of Life, the Universe, and Everything" do
      @thing.answer = 24
      @thing.enforce
      @thing.answer.should be 42 5
    end
    
    end

    1

    Load the tested file (you don’t need to specify the .rb extension)

    2

    describe MyClass do

    describe declares what is being tested

    3

    You can specify code to be run before (before) and after (after). You can execute code before each example with before(:all)

    4

    Each example begins with it followed by a string describing the behavior to be tested

    5

    object.should and object.should_not are used to compare actual and expected values

    examples:

    • o.should == <value> and o.should be <value> are equivalent
    • o.should be_close <value>, <tolerance> tests if a value is within a certain range
    • o.should match <regexp> tests if the value matches a regular expression
    • o.should raise_error tests if a method raises an error (you can specify the type of error as arguments)
    • o.should have (5).things tests if a collection contains 5 items exactly. have_at_least and have_at_most are also available

7.7. JavaScript

  • Since Selenium is based on JavaScript, you can use it in your tests
  • It is an excellent way to extract information from the pages for later reuse
  • There are 2 methods available:

    • getEval(script): it takes the script as a string, executes it and return the value to which the script evaluates too. In ruby use get_eval or js_eval

      • Using this method, the window object refers to the Selenium context, which is different from the tested application’s JavaScript context

        To work around this, you need to get the application’s window object using currentwindow = this.browserbot.getCurrentWindow() * If you want to get an element, just use element = this.browserbot.findElement("locator")

    • runScript(script): it actually adds a <script> tag in the current page of the tested application, making it easier to debug

7.8. Fixtures

  • Usually, fixtures designate the environment setup for the test-cases
  • It is a very abstract notion and in practice it could be very different from a project to another depending on the project’s features
  • In practice, fixtures are a set of tools allowing the test of an application under a pre-determined environment that produces previsible results
  • The main motivation is to avoid errors due to changing environments and the side-effects that may occur
  • In our case, Selenium doesn’t provide any support for fixtures so you have to do it by hand using your test framework’s fixtures capabilities
  • RSpec and JUnit have support for fixtures through the set-up methods

7.9. Lab 4 : Improving a test

7.9.1. Current Situation

  • Test is brittle : change in the structure of the table displaying suggestions breaks the test
  • Locators are complex
  • We can’t do Fixtures

7.9.2. How are we going to change that?

  • You will first creates a suggestion
  • Then you will find a way to delete that suggestion using only the id of the delete link
[Tip]Tip

Observe the id of each table row and the corresponding delete link. You should be able to extract the numeric part of the id and use it to target the delete button. Use the following snippet to get the id:

  • Java:

    String script = "var suggestion_ids = new Array();" +
                    "page = selenium.browserbot.getCurrentWindow().document;" +
                    "table = page.getElementById('as_suggestions-content').childNodes[1];" +
                    "var id = table.rows[2].id;" +
                    "if(id && id != undefined) {" +
                    "  suggestion_ids.push(id.match(/[0-9]+/g));" +
                    "}" +
                    "suggestion_ids;";
    
    selenium.getEval(script);
  • Ruby:

    script = "var suggestion_ids = new Array();" +
                 "page = selenium.browserbot.getCurrentWindow().document;" +
                 "table = page.getElementById('as_suggestions-content').childNodes[1];" +
                 "var id = table.rows[2].id;" +
                 "if (id && id != undefined) {" +
                   "suggestion_ids.push(id.match(/[0-9]+/g));" +
                 "}" +
                 "suggestion_ids;"
    suggestion_id = page.js_eval(script)
  • Bonus: Make a loop that creates more suggestions and find a way to iterate over the table to delete them using javascript