Grible Grible
the place where your test data belong

Content

  1. What Grible is for
  2. PostgreSQL VS. JSON
  3. How to install
  4. How to integrate into your framework
  5. Video tutorial

What Grible is for

Grible is designed for the management of test data of the automated test cases built with data-driven approach. It is not an automation framework or an automation tool like Selenium, Appium etc. Basically it is an Excel replacement.

PostgreSQL VS. JSON

There are two possible ways to store your test data with Grible: PostgreSQL database and JSON files.

PostgreSQL version

PostgreSQL version is a typical web application that is installed on a server, has multiple users and stores the data in a database:

PostgreSQL version

Pros:

Cons:

JSON version

JSON version is more like a desktop application, but in the browser. It is installed on each workstation where you want to edit your test data. The data is kept in JSON files within your automation framework. To edit your data files you need to check out (pull) the latest version of your project, edit files with Grible and commit the changes to the version control system.

JSON version

Pros:

Cons:

How to install

0.8.x versions

This Grible is running on Tomcat and has only PostgreSQL version so you need to:

  1. Install JDK (Java Development Kit). At least JDK 7.
  2. Install PostgreSQL on your host and create an empty database for the Grible. At least PostgreSQL 9.1.
  3. Install Apache Tomcat (for Windows 32-bit/64-bit Windows Service Installer is recommended). Set login and password during installation. Launch the Manager application and deploy grible.war. You need at least Tomcat 7.
Launch the Grible: go to 'http://localhost:8080/grible', where 8080 is Tomcat port. You should see the page where you configure your database.

0.9.x versions and later

This Grible is running on built-in Jetty and has PostgreSQL and JSON versions.

For PostgreSQL version you need to install PostgreSQL on your host and create an empty database for the Grible. You need at least PostgreSQL 9.1.

Windows

For Windows it is recommended to install native package (grible-32bit.exe or grible-64bit.exe). Grible is installed as Windows service and can be uninstalled like any other Windows application.

Note: For native packages you need administrator permissions. If you don't have it, consider using grible.war instead.

Ubuntu/Debian

For Ubuntu/Debian systems use Debian package repository of Grible to automate installation and upgrade. To use the repository, first add the key to your system:

wget -q -O - http://pkg.grible.org/debian/grible.org.key | sudo apt-key add -

Then add the following entry in your /etc/apt/sources.list:

deb http://pkg.grible.org/debian ubuntu main

Make sure you don't have JRE6 or JDK6 installed. Required JDK7 will be installed automatically.

Update your local package index, then finally install Grible:

sudo apt-get update
sudo apt-get install grible

All systems

All the operating systems (including Windows and Ubuntu/Debian) can also run Grible without installing it as a service. To do this you need to install JDK (Java Development Kit), download grible.war and run the following command in the terminal:

java -jar grible.war

Just make sure your "java" points to JDK, not JRE. If it points to JRE, add full path to your JDK bin directory. E.g. for Windows that could be:

C:\Program Files\Java\jdk1.7.0_45\bin\java.exe -jar grible.war

After installation

After installation launch the Grible: go to http://localhost:8123. You should see the page where you select PostgreSQL or JSON version and configure Grible if needed.

How to integrate into your framework

There are Grible adaptor libraries for Java and C# projects. If you would like to create the adaptor for another programming language, please, contact us. It is not hard at all :)

First, you need to download the adaptor library and add it to your automation framework project. Then put the grible.xml file (from the grible-adaptor.zip) somewhere within the project and edit it according to the settings of your Grible database instance. Let's assume we need to write the test for creating a user.

Create (or edit) the parent class for all the test classes (let's call it BaseTest) in this way:

import java.util.HashMap;
import java.util.List;
import org.grible.adaptor.AppTypes;
import org.grible.adaptor.GribleSettings;
import org.grible.adaptor.TestTable;
import org.grible.adaptor.errors.ErrorsHandler;

public abstract class BaseTest {
   protected HashMap<String, String> data;
   private TestTable dataTable;

   public BaseTest(String dataTableName) {
      setupGrible();
      this.dataTable = new TestTable(dataTableName);
   }

   @Test
   public void testTemplate() {
      testPreconditions();
      List<HashMap<String, String>> generalTable = dataTable.getGeneralTable();
      for (int iteration = 0; iteration < generalTable.size(); iteration++) {
         this.data = generalTable.get(iteration);
         onExecute();
      }
      testPostconditions();
   }

   private void setupGrible() {
      // Set up Grible errors handler.
      // Here you tell Grible adaptor what to do with unexpected exceptions.
      GribleSettings.setErrorsHandler( new ErrorsHandler() {
         @Override
         public void onAdaptorFail(Exception e) {
            Assert.fail(e.getLocalizedMessage());
         }
      });
      GribleSettings.setAppType(AppTypes.JSON); // or AppTypes.POSTGRESQL
      GribleSettings.setProductPath("data"); // JSON only. Set relative path to JSON files
      GribleSettings.init("grible.xml"); // PostgreSQL only. Initialize Grible database instance
      GribleSettings.setProductName("SomeProduct"); // PostgreSQL only. Set current product name
   }

   private void testPreconditions() {
      TestEnvironment.apply(dataTable.getPreconditionsTable());
   }

   protected abstract void onExecute();

   private void testPostconditions() {
      TestEnvironment.apply(dataTable.getPostconditionsTable());
   }

}
using System;
using System.Collections.Generic;
using GribleAdaptor;
using GribleAdaptor.Errors;
using NUnit.Framework;

namespace YourNameSpace
{
   public abstract class BaseTest
   {
      private TestTable dataTable;

      public BaseTest(string dataTableName)
      {
         SetupGrible();
         this.dataTable = new TestTable(dataTableName);
      }

      protected Dictionary<string, string> Data { get; set; }

      [Test]
      public void TestTemplate()
      {
         TestPreconditions();
         var generalTable = dataTable.GetGeneralTable();
         for (int iteration = 0; iteration < generalTable.Count; iteration++)
         {
            Data = generalTable[iteration];
            OnExecute();
         }
         TestPostconditions();
      }

      private void SetupGrible()
      {
         // Set up Grible errors handler.
         // Here you tell Grible adaptor what to do with unexpected exceptions.

         GribleSettings.ErHandler = new NUnitErrorsHandler();

         // Set Grible type: JSON or POSTGRESQL
         GribleSettings.AppType = AppTypes.JSON;

         // JSON only. Set relative path to JSON files
         GribleSettings.ProductPath = "..\..\Data";

         // PostgreSQL only. Initialize Grible database instance
         GribleSettings.Init("grible.xml");

         // PostgreSQL only. Set current product name
         GribleSettings.ProductName = "SomeProduct";
      }

      private void TestPostconditions()
      {
         var preconditions = dataTable.GetPreconditionsTable();
         TestEnvironment.Apply(preconditions);
      }

      protected abstract void OnExecute();

      private void TestPreconditions()
      {
         var postconditions = dataTable.GetPostconditionsTable();
         TestEnvironment.Apply(postconditions);
      }
   }

   class NUnitErrorsHandler : IErrorsHandler
   {
      public void OnAdaptorFail(Exception e)
      {
         Assert.Fail(e.Message);
      }
   }
}

TestEnvironment class is for applying test pre/post conditions. For web applications these could be deleting users from database, so that in the Create User test we are sure no user with this name is present. For desktop and mobile applications these could be copying files from one location to another, deleting or creating folders that will be used in the test, etc. So TestEnvironment class would look something like this:

import java.util.HashMap;
import java.util.List;
import org.grible.adaptor.DataStorage;

public class TestEnvironment {

   public static void apply(HashMap<String, String> conditions) {
      if (!conditions.isEmpty()) {
         if (conditions.containsKey("DeleteUsersByEmail")) {
            deleteUsersByEmail(conditions.get("DeleteUsersByEmail"));
         }
         if (conditions.containsKey("InsertUsers")) {
            insertUsers(conditions.get("InsertUsers"));
         }
      }
   }

   private static void deleteUsersByEmail(String indexes) {
      List<UserInfo> usersToDelete = DataStorage.getDescriptors(UserInfo.class, indexes);
      for (UserInfo user : usersToDelete) {
         String email = user.getEmail();
         // Actions to delete user by email from application database
      }
   }

   private static void insertUsers(String indexes) {
      List<UserInfo> usersToInsert = DataStorage.getDescriptors(UserInfo.class, indexes);
      for (UserInfo user : usersToInsert) {
         // Actions to insert users to the application database
      }
   }
}
using System.Collections.Generic;
using GribleAdaptor;

namespace YourNameSpace
{
   public class TestEnvironment
   {
      public static void Apply(Dictionary<string, string> conditions)
      {
         if (conditions.Count != 0)
         {
            if (conditions.ContainsKey("DeleteUsersByEmail"))
            {
               DeleteUsersByEmail(conditions["DeleteUsersByEmail"]);
            }
            if (conditions.ContainsKey("InsertUsers"))
            {
               InsertUsers(conditions["InsertUsers"]);
            }
         }
      }

      private static void DeleteUsersByEmail(string indexes)
      {
         var usersToDelete = DataStorage.GetDescriptors<UserInfo>(indexes);
         foreach (UserInfo user in usersToDelete)
         {
            string email = user.Email;
            // Actions to delete user by email from application database
         }
      }

      private static void InsertUsers(string indexes)
      {
         var usersToInsert = DataStorage.GetDescriptors<UserInfo>(indexes);
         foreach (UserInfo user in usersToInsert)
         {
            // Actions to insert users to the application database
         }
      }
   }
}

Where UserInfo class could look something like this (and, btw, could be generated by Grible):

import java.util.HashMap;
import org.grible.adaptor.BaseDescriptor;

public class UserInfo extends BaseDescriptor {
   private String email;
   private String firstName;
   private String lastName;

   public UserInfo(HashMap<String, String> data) {
      super(data);
      this.email = getString("Email");
      this.firstName = getString("FirstName");
      this.lastName = getString("LastName");
   }

   public String getEmail() {
      return email;
   }

   public String getFirstName() {
      return firstName;
   }

   public String getLastName() {
      return lastName;
   }
}
using System.Collections.Generic;
using GribleAdaptor;

namespace YourNameSpace
{
   public class UserInfo : BaseDescriptor
   {
      public string Email { get; private set; }
      public string FirstName { get; private set; }
      public string LastName { get; private set; }

      public UserInfo(Dictionary<string, string> data)
         : base(data)
      {
         Email = GetString("Email");
         FirstName = GetString("FirstName");
         LastName = GetString("LastName");
      }
   }
}

Each test class passes its data table name from grible to BaseTest in the constructor (usually it's the same name as the test) and overrides onExecute() method where the actual test actions are performed. So that is what CreateUser test class would look like:

public class CreateUser extends BaseTest {

   public CreateUser() {
      super("CreateUser");
   }

   @Override
   protected void onExecute() {
      /*
      * Do test actions here.
      *
      * Get parameters with
      * data.get("YourParameterName")
      *
      * Build desriptors from data storages like this:
      *
      * - single:
      * UserInfo user = DataStorage.getDescriptor(UserInfo.class, data.get("SingleUser"));
      *
      * - list of descriptors:
      * List<UserInfo> users = DataStorage.getDescriptors(UserInfo.class, data.get("UsersList"));
      *
      */

   }
}
namespace YourNameSpace
{
   public class CreateUser : BaseTest
   {
      public CreateUser()
         : base("CreateUser")
      { }

      protected override void OnExecute()
      {
         /*
         * Do test actions here.
         *
         * Get parameters with
         * Data["YourParameterName"]
         *
         * Build desriptors from data storages like this:
         *
         * - single:
         * UserInfo user = DataStorage.GetDescriptor<UserInfo>{Data["SingleUser"]);
         *
         * - list of descriptors:
         * List<UserInfo> users = DataStorage.GetDescriptors<UserInfo>(Data["UsersList"]);
         *
         */

      }
   }
}

If "0" value is passed to the DataStorage.getDescriptor() method, an empty descriptor will be created (not null). To make sure descriptor is not empty use isNotEmpty() method.

Video tutorial

Grible presentation on Black Tea Testing conference (in Ukrainian):

If you have any questions/suggestions, please, contact us.