Thursday, 4 April 2013

JUnit Framework - Basics

This blog discusses the basics of JUnit. Ok, let's start with understanding what a framework is?

What is a framework

A framework is a predefined structure that dictates the structure and flow of control of our program. We create our application on top of a framework.
We can also say that a framework is a large collection of pre-written code, to which we add our code to solve a problem or carry out some functionality. Examples of a framework - JUnit, Java Swings, AWT classes etc.

Having seen what a framework is, we can now define JUnit.

What is JUnit

JUnit is a unit testing framework for Java programming language. It is a member of the unit testing framework family, collectively known as xUnit.

We have just seen that a framework is a predefined structure. As a result to be able to use JUnit in our application / program we have to follow the rules of JUnit. We have to follow its predefined annotations to carry out work.

Annotations in JUnit

List of commonly used annotations are:
1)   @Test
2)   @Ignore
3)   @Before
4)   @After
5)   @BeforeClass
6)   @AfterClass
7)   @RunWith(Suite.class)
8)   @SuiteClasses({,})
9)   @Parameters
10) @RunWith(Parameterized.class)

Let's quickly see what these annotations are and what purpose do they serve:

@Test : This annotation depicts a test case. All the methods that are preceded by this annotation are actually test cases.

@Ignore : As the name suggests, this annotation is used when we want to ignore a particular test case. Example:

@Ignore
@Test
public void testFreeTrialSignup()
{
    //test case code here..
}

Now when you execute the file containing the above test case as a JUnit test, this test case will be skipped.

@Before : Method preceded by this annotation will be called once before executing each test case. So if you have 4 test cases in your java file, the @Before annotation method will execute 4 times.

@After : Just like the @Before annotation, method preceded by @After annotation will be called once after executing each test case.

@BeforeClass : Method preceded by this annotation will be called once before executing all test cases in a java file. So if you have 4 test cases in your java file, the @BeforeClass annotation method will execute only once.

@AfterClass : Method preceded by this annotation will be called once after executing all test cases in a java file. So if you have 4 test cases in your java file, the @AfterClass annotation method will execute only once.


@RunWith(Suite.class)
@SuiteClasses({Test1.class, Test2.class})
Let say in your program, you have 3 java files that contains all the test cases. You can execute each file invidually as a JUnit test. If you wish to execute all your test cases in a single go, you will have to execute them as a part of the test suite with the help of the above two annotations. Sample example:

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;


@RunWith(Suite.class)
@SuiteClasses({TestFreeSignUp.class,TestLogin.class,TestNewEvent.class})
public class TestSuite
{

}


@Parameters
@RunWith(Parameterized.class)
The above two annotations are used when ever you want to create parameterized test cases.

Note that the methods preceded by @BeforeClass, @AfterClass & @Parameters must be public and static methods. This is by rule. If you skip any or both the keywords, eclipse will not complain but as you try to run  the test suite, JUnit will throw errors.

Methods preceded by all other annotations listed above must be public methods.

A sweet and simple program that shows the use of some of the annotations listed above is as follows. Copy paste in your eclipse project to see output:

import org.junit.After;

import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

public class testJUnitAnnotations
{

@BeforeClass
public static void atTheRateBeforeClassAnnotationMethod()
{
System.out.println("I am @BeforeClass preceded method. I will run only once." +
" Use me to do initializations.");
}

@Before
public void atTheRateBeforeAnnotationMethod()
{
System.out.println("I am @Before preceded method. I will run each time" +
" before a test is executed.");
}

@After
public void atTheRateAfterAnnotationMethod()
{
System.out.println("I am @After preceded method. I will run each time" +
" after a test case completes execution.");
}

@Test
public void atTheRateTestMehtod1()
{
System.out.println("I am @Test preceded method. I am a test case.");
}

@Ignore
@Test
public void atTheRateIgnoredTestMehtod()
{
System.out.println("I am @Test preceded method. I am an ignored test case.");
}

@Test
public void atTheRateTestMehtod2()
{
System.out.println("I am @Test preceded method. I am a test case.");
}

@AfterClass
public static void atTheRateAfterClassAnnotationMethod()
{
System.out.println("I am @AfterClass preceded method. I will run only once." +
" Use me to do initializations.");
}
}


The output of the program is shown below:

This sums up the JUnit basics tutuorial. Comments are welcome.

Thanks :)

Tuesday, 2 April 2013

Why need a data driven framework?

Understanding Data Driven Testing

Data driven testing generally means executing tests by using multiple sets of data which is separate from test cases. Let us try to understand it with the help of an example:

We need to write a test for let say a sign-up page that asks for 3 things:
1) First Name
2) Last Name
3) Password

Let's try to find out some scenario's for the above mentioned situation:

Scenario 1:   First name is blank. Last name and password are provided.

Scenario 2:   First name and password is provided. Last name left blank.
Scenario 3:   First and last name are provided. Password is not provided.

Now there are two routes to achieve the above functionality:

Route 1: We can write individual tests for each of the scenario above.
Route 2: We can write a single test with different test data for each scenario.

Route 2 is preferable as there will be only one test that will deal with all the scenario's. Here is how the test would be written traditionally:

@Test
public void testSignUp()
{
      //Scenario 1
      String firstName = "";
      String lastName = "X";
      String password = "10";
      
      driver.findElement(By.xpath("111")).sendKeys(firstName);
      driver.findElement(By.xpath("222")).sendKeys(lastName);
      driver.findElement(By.xpath("333")).sendKeys(password);

      driver.findElement(By.xpath("login button")).click();

        
      //Scenario 2
      firstName = "A";
      lastName = "";
      password = "10";
      
      driver.findElement(By.xpath("111")).sendKeys(firstName);
      driver.findElement(By.xpath("222")).sendKeys(lastName);
      driver.findElement(By.xpath("333")).sendKeys(password);

      driver.findElement(By.xpath("login button")).click();


      //Scenario 3
      firstName = "A";
      lastName = "X";
      password = "";
      
      driver.findElement(By.xpath("111")).sendKeys(firstName);
      driver.findElement(By.xpath("222")).sendKeys(lastName);
      driver.findElement(By.xpath("333")).sendKeys(password);

      driver.findElement(By.xpath("login button")).click();
}

Few observations from the above code:

1) Values are hard coded which is not advisable. Tightly coupled data.
2) Clearly lots of duplicate code. 
3) There can be more scenarios. What if first name, last name and password, all three are provided or all three are not provided. 

To handle new scenario's more code would need to be added which will further lead to complexity and more duplicate code. Inefficient isn't it?

Clearly the above code cannot be considered to be data driven since the data is tightly coupled. Any change in the test data will result in changing the test case.

There is a need to make this test case generic so that it could execute variety of test data. Clearly we need to take the data out of the test case.

To do that we need to follow data driven automation framework by using JUnit's Parameterized runner functionality.

Please provide comments so that this blog can futher be refined.

Thanks :)